I'm implementing a feature so the user can paint freely in a canvas in the screen. The expected behaviour is working very nicely: I can change the brush size and color and erase the drawing thanks to the call to canvas.saveLayer and canvas.restore.
The main issue happens when I've accumulated enough drawings to make the paint method very expensive to the application. I don't think adding RepaintBoundary will actually improve this, because so far the method is repainting the whole drawing as the user keeps drawing and its performance gets progressively worse, reaching 1 fps if you push so far.
Here is the code:
class DrawingCanvas extends StatefulWidget {
const DrawingCanvas({
super.key,
required this.onPanUpdate,
required this.onPanEnd,
required this.initialDrawings,
required this.selectedPainter,
this.boundarySize = Size.infinite,
});
final List<DrawingDetails> initialDrawings;
final void Function(Offset?) onPanUpdate;
final void Function(Offset?) onPanEnd;
final Paint selectedPainter;
final Size boundarySize;
@override
State<DrawingCanvas> createState() => _DrawingCanvasState();
}
class _DrawingCanvasState extends State<DrawingCanvas> {
@override
Widget build(BuildContext context) {
return GestureDetector(
onPanStart: (details) {
setState(() {
widget.onPanUpdate(details.localPosition);
});
},
onPanUpdate: (details) {
setState(() {
widget.onPanUpdate(details.localPosition);
});
},
onPanEnd: (details) {
setState(() {
widget.onPanEnd(null);
});
},
child: CustomPaint(
isComplex: true,
painter: _DrawingPainter(
initialDrawings: widget.initialDrawings,
selectedPainter: widget.selectedPainter,
boundarySize: widget.boundarySize,
),
size: Size.infinite,
),
);
}
}
class _DrawingPainter extends CustomPainter {
final List<DrawingDetails> initialDrawings;
final Paint selectedPainter;
final Logger logger = Logger('_DrawingPainter');
final Size boundarySize;
_DrawingPainter({
required this.initialDrawings,
required this.selectedPainter,
required this.boundarySize,
});
@override
bool shouldRepaint(_DrawingPainter oldDelegate) {
return true;
}
void paint(Canvas canvas, Size size) {
canvas.saveLayer(Rect.largest, Paint());
for (DrawingDetails drawing in initialDrawings) {
for (int i = 0; i < drawing.points.length - 1; i++) {
if (drawing.points[i] != null && drawing.points[i + 1] != null) {
canvas.drawLine(
drawing.points[i]!,
drawing.points[i + 1]!,
drawing.painterOptions.painter,
);
}
}
}
canvas.restore();
}
}
I've also tried to avoid adding the same offset to the list if it is repeated, but it improves just a little bit, but doesn't keep the issue from happening.
Anyone has a suggestion on how I can solve this issue? Is there any improvement I am oblivious to?
Canvas.drawLinetry to convert after each (for example) 1000 points your canvas toImage- you do that withCanvas(PictureRecorder)ctor and then calling 1000 timesCanvas.drawLinefollowed byPicure.toImage[Sync]- now your 1000 points can be drawn with simpleCnavas.drawImagePointMode.polygon?