扑扑新手。我目前正在尝试使用Flutter构建一个简单的触摸绘图应用程序,但无法确定如何触发画布重新绘制。
我所拥有的是:我有一个CustomPaint小部件,其中包含GestureDetector子级。每当发生触摸事件时,CustomPaint的绘画者都会收到一条消息,并存储触摸坐标以在重新绘制时绘制路径。问题是,永远不会调用paint方法。
这是我到目前为止的代码:
import 'package:flutter/material.dart'; class WriteScreen extends StatefulWidget { @override _WriteScreenState createState() => new _WriteScreenState(); } class KanjiPainter extends CustomPainter { Color strokeColor; var strokes = new List<List<Offset>>(); KanjiPainter( this.strokeColor ); void startStroke(Offset position) { print("startStroke"); strokes.add([position]); } void appendStroke(Offset position) { print("appendStroke"); var stroke = strokes.last; stroke.add(position); } void endStroke() { } @override void paint(Canvas canvas, Size size) { print("paint!"); var rect = Offset.zero & size; Paint fillPaint = new Paint(); fillPaint.color = Colors.yellow[100]; fillPaint.style = PaintingStyle.fill; canvas.drawRect( rect, fillPaint ); Paint strokePaint = new Paint(); strokePaint.color = Colors.black; strokePaint.style = PaintingStyle.stroke; for (var stroke in strokes) { Path strokePath = new Path(); // Iterator strokeIt = stroke.iterator..moveNext(); // Offset start = strokeIt.current; // strokePath.moveTo(start.dx, start.dy); // while (strokeIt.moveNext()) { // Offset off = strokeIt.current; // strokePath.addP // } strokePath.addPolygon(stroke, false); canvas.drawPath(strokePath, strokePaint); } } bool shouldRepaint(covariant CustomPainter oldDelegate) { return true; } } class _WriteScreenState extends State<WriteScreen> { GestureDetector touch; CustomPaint canvas; KanjiPainter kanjiPainter; void panStart(DragStartDetails details) { print(details.globalPosition); kanjiPainter.startStroke(details.globalPosition); } void panUpdate(DragUpdateDetails details) { print(details.globalPosition); kanjiPainter.appendStroke(details.globalPosition); } void panEnd(DragEndDetails details) { kanjiPainter.endStroke(); } @override Widget build(BuildContext context) { touch = new GestureDetector( onPanStart: panStart, onPanUpdate: panUpdate, onPanEnd: panEnd, ); kanjiPainter = new KanjiPainter( const Color.fromRGBO(255, 255, 255, 1.0) ); canvas = new CustomPaint( painter: kanjiPainter, child: touch, // child: new Text("Custom Painter"), // size: const Size.square(100.0), ); Container container = new Container( padding: new EdgeInsets.all(20.0), child: new ConstrainedBox( constraints: const BoxConstraints.expand(), child: new Card( elevation: 10.0, child: canvas, ) ) ); return new Scaffold( appBar: new AppBar( title: new Text("Draw!") ), backgroundColor: const Color.fromRGBO(200, 200, 200, 1.0), body: container, ); } }
根据CustomPainter docs,您必须在需要重绘时通知油漆小部件
触发重画的最有效方法是扩展此类并向CustomPainter的构造函数提供重画参数,该对象在需要重画时通知其侦听器,或扩展Listenable(例如,通过ChangeNotifier)并实现CustomPainter,以便对象本身直接提供通知。在这两种情况下,无论何时勾选动画,CustomPaint小部件或RenderCustomPaint渲染对象都将侦听Listenable并重新绘制,从而避免了管道的构建和布局阶段。
例如,KanjiPainter应该扩展ChangeNotifier并实施CustomPainter。当您更改笔触时,notifyListeners
KanjiPainter
ChangeNotifier
CustomPainter
notifyListeners
并且build函数总是创建new KanjiPainter,这将删除所有旧数据。您可以initState一次启动画家。
build
initState
工作示例:
class WriteScreen extends StatefulWidget { @override _WriteScreenState createState() => new _WriteScreenState(); } class KanjiPainter extends ChangeNotifier implements CustomPainter { Color strokeColor; var strokes = new List<List<Offset>>(); KanjiPainter(this.strokeColor); bool hitTest(Offset position) => null; void startStroke(Offset position) { print("startStroke"); strokes.add([position]); notifyListeners(); } void appendStroke(Offset position) { print("appendStroke"); var stroke = strokes.last; stroke.add(position); notifyListeners(); } void endStroke() { notifyListeners(); } @override void paint(Canvas canvas, Size size) { print("paint!"); var rect = Offset.zero & size; Paint fillPaint = new Paint(); fillPaint.color = Colors.yellow[100]; fillPaint.style = PaintingStyle.fill; canvas.drawRect(rect, fillPaint); Paint strokePaint = new Paint(); strokePaint.color = Colors.black; strokePaint.style = PaintingStyle.stroke; for (var stroke in strokes) { Path strokePath = new Path(); // Iterator strokeIt = stroke.iterator..moveNext(); // Offset start = strokeIt.current; // strokePath.moveTo(start.dx, start.dy); // while (strokeIt.moveNext()) { // Offset off = strokeIt.current; // strokePath.addP // } strokePath.addPolygon(stroke, false); canvas.drawPath(strokePath, strokePaint); } } bool shouldRepaint(covariant CustomPainter oldDelegate) { return true; } } class _WriteScreenState extends State<WriteScreen> { GestureDetector touch; CustomPaint canvas; KanjiPainter kanjiPainter; void panStart(DragStartDetails details) { print(details.globalPosition); kanjiPainter.startStroke(details.globalPosition); } void panUpdate(DragUpdateDetails details) { print(details.globalPosition); kanjiPainter.appendStroke(details.globalPosition); } void panEnd(DragEndDetails details) { kanjiPainter.endStroke(); } @override void initState() { super.initState(); kanjiPainter = new KanjiPainter(const Color.fromRGBO(255, 255, 255, 1.0)); } @override Widget build(BuildContext context) { touch = new GestureDetector( onPanStart: panStart, onPanUpdate: panUpdate, onPanEnd: panEnd, ); canvas = new CustomPaint( painter: kanjiPainter, child: touch, // child: new Text("Custom Painter"), // size: const Size.square(100.0), ); Container container = new Container( padding: new EdgeInsets.all(20.0), child: new ConstrainedBox( constraints: const BoxConstraints.expand(), child: new Card( elevation: 10.0, child: canvas, ))); return new Scaffold( appBar: new AppBar(title: new Text("Draw!")), backgroundColor: const Color.fromRGBO(200, 200, 200, 1.0), body: container, ); } }