Drawing a task completion ring with the canvas


We can use the canvas to draw custom shapes.

Each draw command needs a Paint object. This can be configured to use PaintingStyle.fill or PaintingStyle.stroke. We can configure it with the cascade operator:

final backgroundPaint = Paint()
  ..strokeWidth = strokeWidth
  ..color = Colors.black
  ..style = PaintingStyle.stroke;

In this case, we need to draw a circle given a center and radius that are calculated from the size argument:

canvas.drawCircle(center, radius, paint);

We can also draw an arc like this:

  Rect.fromCircle(center: center, radius: radius),
  -pi / 2,
  pi * 0.5,

We can use the covariant keyword when overriding the shouldRepaint() method, so that the given argument matches the same class and it's easier to access its properties:

bool shouldRepaint(covariant RingPainter oldDelegate) =>
      oldDelegate.progress != progress;

Additional notes

  • we can implement a CustomPainter when any of the existing Flutter widgets are not enough for our purposes
  • we can do this by extending the CustomPainter class and implementing the paint() and shouldRepaint() methods.
  • the paint() method gives us a canvas that we can use to draw shapes, and all the available drawing methods take a Paint object that we can use to customise the appearance of our shapes
  • if we want to draw shapes that are proportional to the size of the parent widget, we can create variables that depend on the size argument
  • it's a good idea to make our painters customisable by passing some values as arguments, just like we would do if we were creating custom widgets.
  • we can implement the shouldRepaint method in such a way that it only returns true when something changes (this helps with performance).

Complete and Continue