Efficient Canvas rendering in Flutter

1,393

This is the Controller class

class DrawingController extends ChangeNotifier {

  List<Offset> _points = []
   
  .... // Perform operations on data points
  ....

  void add(Offset point) {
    _points.add(point);
    notifyListeners();
  }

}

This is the CustomPaint class

class Painter extends CustomPainter {
  DrawingController controller;

  Painter(this.controller) : super(repaint: controller); //This is important

  @override
  void paint(Canvas canvas, Size size) {
     //Paint function
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) => true;
}

And in the Gesture detector simply call controller.add(point) to add new points as you grab them

I am not sure this is the most efficient way to paint but it reduced the paint times down to 13ms on a 60hz screen and 9ms on a 120Hz screen. One noticeable drawback is that a single stroke (onTapdownEvent to an onDragEndEvent) renders quite slowly(up to 18ms) when the stroke has many points. This problem disappears as soon as you start a new stroke. I tried asking on several forums and this is the best I could come up with after digging through the flutter source code.

I profiled this functionality on a snapdragon 855 device so the processor is not a bottleneck. If anyone finds a better solution please post it.

Share:
1,393
Dhruva
Author by

Dhruva

Updated on November 28, 2022

Comments

  • Dhruva
    Dhruva over 1 year

    I have been trying to develop a drawing app and all the example codes including the one's from The Boring Flutter Development Show don't translate well into real-world usage.

    The main problem being that CustomPaint's paint operation is too expensive and re-draws every point, every frame. And as the points increase the flutter app's render time per frame increases significantly

    From the time I spent finding a solution to this problem, I found these

    1. RepaintBoundary : Rasterizes layers

    2. Custom Widget using SingleChildRenderObjectWidget and RenderProxyBox : must implement a paint method and no way of passing a controller

    I don't think any of the above solutions work well for my needs: smooth canvas drawing operations without re-paint. I even tried simplifying the points and that didn't work either because of the inherent mechanism of CustomPaint

    If there was a way to pass a canvas as a widget and attaching a controller it'll be easy to store the captured points and use basic canvas operations like canvas.drawPath() or canvas.drawLine() much efficiently

    Any suggestion would be helpful. Thank you!

    • pskink
      pskink almost 4 years
      PictureRecorder maybe?
    • Dhruva
      Dhruva almost 4 years
      @pskink from all the examples I have seen, I couldn't find any example which passed around either PictureRecorder or Canvas as a Widget where you can render real-time touch events. I apologise if I sounded naive as I started learning flutter recently.
    • Alex
      Alex over 3 years
      @Dhruva have you found a solution?
    • Dhruva
      Dhruva over 3 years
      @Alex I will post an answer