PK creative Spring Festival, I am participating in the “Spring Festival creative submission contest”, please see: Spring Festival creative submission Contest

This morning, I woke up to find that alipay’s annual activity of collecting five blessings has started again. This activity includes the function of writing blessings by hand, revoking a stroke, erasing a rewrite, saving albums, etc. How should Flutter achieve these functions?

demand

Specific requirements include:

  • The interface slides with the user’s finger to show the track, that is, the corresponding stroke.
  • Click the Clear button to clear all strokes.
  • Click the undo button to clear the strokes drawn in the previous step.
  • Save the written text style to the album.

Implementation approach

Display stroke trace

Use the Listener component to listen for the user’s finger drops, slides, and drops. In onPointerDown onPointerMove, onPointerUp3 monitoring method in the returned PointerMoveEvent object contains the finger position coordinate offset localPosition, users will record every time sliding track after the coordinates of the point, These coordinate points together form a line. Secondly, with the help of CustomPainter, we can draw the canvas by drawing all the dots into lines and using brushes on the interface.

Collect coordinate points:

Listener( child: Container( alignment: Alignment.center, color: Colors.transparent, width: double.infinity, height: MediaQuery.of(context).size.height, ), onPointerDown: (PointerDownEvent event) { setState(() { }); }, onPointerMove: (PointerMoveEvent event) { setState(() { }); }, onPointerUp: (PointerUpEvent event) {setState(() {}); },),Copy the code

Drawing:

@override void paint(Canvas canvas, Size size) { myPaint.strokeCap = StrokeCap.round; MyPaint. StrokeWidth = 15.0; if (lines.isEmpty) { canvas.drawPoints(PointMode.polygon, [Offset.zero, Offset.zero], myPaint); } else { for (int k = 0; k < lines.length; k++) { for (int i = 0; i < lines[k].length - 1; i++) { if (lines[k][i] ! = Offset.zero && lines[k][i + 1] ! = Offset.zero) { canvas.drawLine(lines[k][i], lines[k][i + 1], myPaint); } } } } }Copy the code

Undo and Clear

Looking at the code above, some might wonder why it is so complicated to draw, with a double loop. This is related to the undo function, assuming that there is no need for the undo function, in fact, we can directly connect all the stroke points together to draw, but once the undo function is introduced, we need to record every stroke, fu character stroke is 13 strokes, so in theory, we need to record 13 strokes. In order to ensure that each undo can normally return to the last drawn handwriting, so the first reaction is to use the collection to record each stroke. It is also said above that each stroke is actually a set of multiple coordinate points, so all strokes are a set of coordinate points, namely:

List<List<Offset>> _lines = [];Copy the code

In addition, it is not difficult to imagine that we can easily distinguish the beginning and end of a stroke by finger press and finger callbacks. Add and update strokes in both methods.

onPointerDown: (PointerDownEvent event) { setState(() { _event = event; _points.add(_event? .localPosition ?? Offset.zero); _lines.add(_points); }); }, onPointerMove: (PointerMoveEvent event) { setState(() { _event = event; _points.add(_event? .localPosition ?? Offset.zero); _lines.last = _points; }); }, onPointerUp: (PointerUpEvent event) { setState(() { _event = event; _points.add(Offset.zero); _lines.last = _points; }); _points = []; },Copy the code

And the previous said double traversal at this time also better understand:

  • The first cycle is to traverse all strokes, and the number of strokes traversed is the number of strokes of the character fu.
  • The second layer of loop is each stroke includes a number of coordinate points, traversal out to usedrawLineMethod is drawn to the interface to form a line.

When doing undo, call removeLast on list to remove the last item and refresh the screen to achieve the effect of a stroke back. Clear is to clear the stroke collection.

Save to album

The main way to save albums is to introduce two plug-in libraries: permission_handler and image_gallery_saver, one for obtaining storage permissions and the other for saving to albums. Use the RepaintBoundary component to wrap the canvas and specify the key. When clicking Save, call the following methods in sequence to obtain the screenshot and then save:

RenderRepaintBoundary boundary = key.currentContext! .findRenderObject() as RenderRepaintBoundary; Var image = await boundary. ToImage (pixelRatio: 3.0); ByteData? byteData = await image.toByteData(format: ImageByteFormat.png); _postBytes = byteData? .buffer.asUint8List(); var result = await ImageGallerySaver.saveImage(_postBytes!) ;Copy the code

Complete code and demo download

Making the address

Android phone scan code download