flutter 팔레트 2.0 지우개

손으로 쓴 것을 실현하는 기초 위에서 지우개 기능을 추가하다
import 'dart:ui';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'dart:ui' as UI;

class HandWrittenBoard extends StatefulWidget {
  ///     
  final Color? painColor;

  ///     
  final double? paintWidth;

  ///  
  final double? clearWidth;

  final double? clearDx;

  final double? clearDy;

  ///      
  final HandWrittenBoardController boardController;

  const HandWrittenBoard(
      {Key? key,
      this.painColor,
      this.clearWidth,
      this.paintWidth,
      required this.boardController,
      this.clearDx,
      this.clearDy})
      : super(key: key);

  @override
  _HandWrittenBoardState createState() => _HandWrittenBoardState();
}

class HandWrittenBoardController extends ChangeNotifier {
  late BuildContext _context;
  late List strokes = [];

  void bindContext(BuildContext context) {
    _context = context;
  }

  Future? get uiImage {
    UI.PictureRecorder recorder = UI.PictureRecorder();
    Canvas canvas = Canvas(recorder);
    BoardPainter painter = BoardPainter();
    Size size = _context.size!;
    painter.paint(canvas, size);
    return recorder
        .endRecording()
        .toImage(size.width.floor(), size.height.floor());
  }

  void refStrokes(List newValue) {
    if (strokes != newValue) {
      strokes = newValue;
    }
    notifyListeners();
  }

  void clearBoard() {
    strokes.clear();
    notifyListeners();
  }
}

class _HandWrittenBoardState extends State {
  List _strokes = [];

  @override
  void initState() {
    super.initState();
    widget.boardController.bindContext(context);
  }

  void _onUpdate(DragUpdateDetails details) {
    if (isClear) {
      dx = details.localPosition.dx;
      dy = details.localPosition.dy;
    }
    setState(() {
      _strokes.last.path
          .lineTo(details.localPosition.dx, details.localPosition.dy);
    });
    widget.boardController.refStrokes(_strokes);
  }

  void _start(double startX, double startY) {
    if (isClear) {
      dx = startX;
      dy = startY;
    }

    final newStroke = Stroke(
      color: widget.painColor!,
      width: widget.paintWidth!,
      isClear: isClear,
    );
    newStroke.path.moveTo(startX, startY);
    _strokes.add(newStroke);
    widget.boardController.refStrokes(_strokes);
  }

  late double dx = widget.clearDx! + widget.clearWidth! / 2;
  late double dy = widget.clearDy! - widget.clearWidth! / 2;

  bool isClear = false;

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onPanUpdate: _onUpdate,
      onPanStart: (details) => _start(
        details.localPosition.dx,
        details.localPosition.dy,
      ),
      onPanDown: (detail) {
        Rect rect =
            Rect.fromCenter(center: Offset(dx, dy), width: 50, height: 50);
        isClear = rect.contains(detail.localPosition);
      },
      child: Stack(
        children: [
          CustomPaint(
            painter: BoardPainter(
              strokes: _strokes,
              clearWidth: widget.clearWidth,
            ),
            size: Size.infinite,
          ),
          Positioned(
            child: Container(
              width: widget.clearWidth,
              height: widget.clearWidth,
              color: Colors.red,
            ),
            left: dx - widget.clearWidth! / 2,
            top: dy - widget.clearWidth! / 2,
          ),
        ],
      ),
    );
  }
}

class Stroke {
  final path = Path();
  final Color color;
  final double width;
  final bool isClear;

  Stroke({
    this.color = Colors.black,
    this.width = 4,
    this.isClear = false,
  });
}

class BoardPainter extends CustomPainter {
  BoardPainter({
    this.strokes,
    this.clearWidth,
  });

  final List? strokes;
  final double? clearWidth;

  @override
  void paint(Canvas canvas, Size size) {
    canvas.clipRect(Rect.fromLTWH(0, 0, size.width, size.height));

    canvas.drawRect(
      Rect.fromLTWH(0, 0, size.width, size.height),
      Paint()..color = Colors.white,
    );
    canvas.saveLayer(Rect.fromLTWH(0, 0, size.width, size.height), Paint());
    for (final stroke in strokes!) {
      final paint = Paint()
        ..strokeWidth = stroke.isClear ? clearWidth! : stroke.width
        ..color = stroke.isClear ? Colors.transparent : stroke.color
        ..strokeCap = StrokeCap.round
        ..style = PaintingStyle.stroke
        ..blendMode = stroke.isClear ? BlendMode.clear : BlendMode.srcOver;
      canvas.drawPath(stroke.path, paint);
    }
    canvas.restore();
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return true;
  }
}


사용 시범
import 'package:flitter_okgo/hand_written_board.dart';
import 'package:flutter/material.dart';

class HandWrittenBoardPage extends StatefulWidget {
  @override
  _TestPageState createState() => _TestPageState();
}

class _TestPageState extends State {
  HandWrittenBoardController controller = HandWrittenBoardController();

  @override
  void initState() {
    super.initState();
  }

  Widget get initFloatingActionButton {
    return FloatingActionButton(
      backgroundColor: Colors.grey,
      elevation: 1,
      focusElevation: 1,
      onPressed: () {
        controller.clearBoard();
      },
      child: Icon(Icons.clear),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: initFloatingActionButton,
      floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
      appBar: AppBar(
        title: Text("   "),
      ),
      body: HandWrittenBoard(
        boardController: controller,
        clearWidth: 50,
        paintWidth: 5,
        painColor: Colors.green,
        clearDx: 0,
        clearDy: 200,
      ),
    );
  }
}

좋은 웹페이지 즐겨찾기