What is CustomPainter used for?
Short Answer
CustomPainter lets you draw directly onto a canvas with low-level drawing primitives (paths, arcs, text, gradients) for visuals that no combination of standard widgets can produce — charts, custom shapes, signature pads.
You subclass `CustomPainter`, implement `paint(Canvas canvas, Size size)` with direct drawing calls (`canvas.drawPath`, `canvas.drawCircle`, `canvas.drawLine`, etc.), and implement `shouldRepaint(oldDelegate)` to tell Flutter whether it needs to repaint when something changes — returning `false` when nothing relevant changed avoids wasted repaints. You attach it to the tree via a `CustomPaint` widget, optionally giving it a `size` and a `child` to paint over/under.
This is the right tool when you need pixel-level control that widgets can't express — a custom progress ring, a chart, a hand-drawn signature capture, or a complex clipped/gradient shape. For most "slightly custom" visuals, reach for `ClipPath`, `ShaderMask`, or `DecoratedBox` first; CustomPainter is for when those genuinely aren't enough.
Code Example
class RingPainter extends CustomPainter {
RingPainter(this.progress);
final double progress; // 0.0 - 1.0
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.blue
..style = PaintingStyle.stroke
..strokeWidth = 8;
final rect = Rect.fromLTWH(0, 0, size.width, size.height);
canvas.drawArc(rect, -1.57, progress * 6.28, false, paint);
}
@override
bool shouldRepaint(RingPainter old) => old.progress != progress;
}Common Mistakes
- ×Always returning `true` from `shouldRepaint`, forcing a repaint on every frame regardless of whether anything changed.
- ×Doing expensive computation inside `paint()` instead of precomputing it before passing data to the painter.