Bird
Raised Fist0
Fluttermobile~15 mins

Custom painters (CustomPaint) in Flutter - Deep Dive

Choose your learning style10 modes available
Overview - Custom painters (CustomPaint)
What is it?
Custom painters in Flutter let you draw shapes, lines, and images directly on the screen. They give you full control over how things look by painting on a canvas. This is useful when built-in widgets can't create the exact design you want. You write code that tells Flutter how to draw each pixel.
Why it matters
Without custom painters, you would be limited to standard widgets and their styles. Custom painting lets you create unique graphics, animations, and visual effects that make your app stand out. It solves the problem of needing precise control over drawing beyond simple buttons or text.
Where it fits
Before learning custom painters, you should understand Flutter widgets and the basics of drawing with widgets like Container and Stack. After mastering custom painters, you can explore animations, gesture detection on custom drawings, and advanced graphics with shaders.
Mental Model
Core Idea
Custom painters let you draw directly on a canvas by writing code that controls every pixel's appearance.
Think of it like...
It's like having a blank canvas and paintbrush where you decide exactly what to paint, instead of using pre-made stickers or stamps.
┌───────────────┐
│ Flutter Widget│
│ Tree          │
│               │
│  ┌─────────┐  │
│  │Custom   │  │
│  │Paint    │  │
│  │Widget   │  │
│  └─────────┘  │
│       │       │
│       ▼       │
│  ┌─────────┐  │
│  │Canvas   │  │
│  │(Draw)   │  │
│  └─────────┘  │
└───────────────┘
Build-Up - 7 Steps
1
FoundationWhat is CustomPaint Widget
🤔
Concept: Introducing the CustomPaint widget that holds a painter to draw on the screen.
CustomPaint is a Flutter widget that provides a canvas to draw on. You give it a CustomPainter object that has a paint method. Flutter calls this method to draw your shapes or images. The CustomPaint widget fits into the widget tree like any other widget.
Result
You get a blank area on the screen where your painter can draw anything you want.
Understanding that CustomPaint is just a widget helps you see it fits naturally into Flutter's UI system.
2
FoundationBasics of CustomPainter Class
🤔
Concept: Learning how to create a CustomPainter by overriding paint and shouldRepaint methods.
To create a custom painter, extend CustomPainter and override two methods: paint(Canvas canvas, Size size) and shouldRepaint(CustomPainter oldDelegate). The paint method is where you draw using the canvas. The shouldRepaint method tells Flutter when to redraw.
Result
You can write code inside paint to draw lines, shapes, or text on the canvas.
Knowing the paint method is your drawing playground is key to mastering custom painting.
3
IntermediateDrawing Basic Shapes on Canvas
🤔Before reading on: do you think you can draw a circle with just one line of code or do you need multiple steps? Commit to your answer.
Concept: Using the Canvas API to draw shapes like circles, rectangles, and lines.
Inside the paint method, use canvas.drawCircle, canvas.drawRect, or canvas.drawLine with Paint objects to set colors and styles. For example, canvas.drawCircle(Offset(x, y), radius, paint) draws a circle at position (x,y).
Result
Your app shows the shapes you coded, rendered exactly where and how you specified.
Understanding the canvas API unlocks the ability to create any shape or design you imagine.
4
IntermediateControlling Paint Styles and Colors
🤔Before reading on: do you think Paint only controls color or can it also control stroke width and fill style? Commit to your answer.
Concept: Using the Paint class to customize how shapes are drawn, including color, stroke width, and fill.
Paint lets you set color, stroke width, stroke or fill style, and more. For example, paint.color = Colors.blue; paint.style = PaintingStyle.stroke; paint.strokeWidth = 4.0; changes how shapes appear. This controls whether shapes are filled or just outlines.
Result
Shapes appear with the colors and styles you set, making your drawings look polished and intentional.
Knowing how to customize Paint lets you create visually rich and varied graphics.
5
IntermediateHandling Size and Position in Painting
🤔Before reading on: do you think the canvas size is fixed or does it depend on the widget size? Commit to your answer.
Concept: Using the Size parameter to adapt drawings to the widget's size and positioning elements correctly.
The paint method receives a Size object representing the widget's width and height. Use this to position shapes relative to the widget size, so your drawing scales well on different screen sizes. For example, draw a circle at Offset(size.width/2, size.height/2) to center it.
Result
Your drawings adjust to different screen sizes and orientations, looking good everywhere.
Using Size ensures your custom painting is responsive and flexible.
6
AdvancedOptimizing Repaints with shouldRepaint
🤔Before reading on: do you think shouldRepaint always returns true or can it return false sometimes? Commit to your answer.
Concept: Controlling when Flutter redraws your custom painter to improve performance.
The shouldRepaint method compares the old painter with the new one. Return true only if the drawing needs updating. Returning false avoids unnecessary redraws, saving CPU and battery. For example, compare properties that affect drawing and return true if they changed.
Result
Your app runs smoother because Flutter skips redrawing when nothing changed.
Knowing how to control repainting prevents performance issues in complex apps.
7
ExpertCombining CustomPaint with Animations
🤔Before reading on: do you think CustomPaint can animate drawings by itself or needs help from other widgets? Commit to your answer.
Concept: Using CustomPaint with animation controllers to create dynamic, moving graphics.
CustomPaint itself only draws static images. To animate, use an AnimationController and call setState or repaint the painter when the animation value changes. Pass the animation value to the painter to update the drawing each frame. This creates smooth animations like progress bars or waves.
Result
Your app shows animated custom drawings that respond to time or user input.
Understanding how to link CustomPaint with animations unlocks powerful, custom visual effects.
Under the Hood
Flutter's rendering engine calls the paint method of your CustomPainter whenever it needs to draw or redraw the widget. The Canvas object represents a low-level drawing surface where you can issue commands to draw shapes, text, or images. Flutter batches these commands and renders them efficiently on the screen. The shouldRepaint method helps Flutter decide if it can reuse the existing drawing or must call paint again.
Why designed this way?
CustomPaint was designed to give developers fine-grained control over drawing while fitting into Flutter's widget system. It separates the drawing logic (CustomPainter) from layout and composition (widgets). This design allows efficient redrawing and easy integration with Flutter's reactive model. Alternatives like direct pixel manipulation would be slower and harder to manage.
┌───────────────┐
│ Flutter Widget│
│ Tree          │
│               │
│  ┌─────────┐  │
│  │Custom   │  │
│  │Paint    │  │
│  │Widget   │  │
│  └─────────┘  │
│       │       │
│       ▼       │
│  ┌─────────┐  │
│  │Flutter  │  │
│  │Rendering│  │
│  │Engine   │  │
│  └─────────┘  │
│       │       │
│       ▼       │
│  ┌─────────┐  │
│  │Canvas   │  │
│  │Commands │  │
│  └─────────┘  │
│       │       │
│       ▼       │
│  ┌─────────┐  │
│  │GPU/CPU  │  │
│  │Draw     │  │
│  └─────────┘  │
└───────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Does CustomPaint automatically animate your drawings without extra code? Commit yes or no.
Common Belief:CustomPaint automatically animates drawings when you change properties.
Tap to reveal reality
Reality:CustomPaint only draws static images; you must use animation controllers and trigger repaints to animate.
Why it matters:Believing this causes confusion and wasted time trying to animate without proper setup.
Quick: Can you use setState inside CustomPainter to update drawings? Commit yes or no.
Common Belief:You can call setState inside the CustomPainter class to update the UI.
Tap to reveal reality
Reality:CustomPainter is not a widget and cannot call setState; you must manage state outside and pass data in.
Why it matters:Misunderstanding this leads to errors and broken app logic.
Quick: Does returning false from shouldRepaint mean your drawing never updates? Commit yes or no.
Common Belief:If shouldRepaint returns false, the drawing will never update again.
Tap to reveal reality
Reality:Returning false means Flutter reuses the old painting; if your data changed, you must return true to update.
Why it matters:Returning false incorrectly causes stale visuals and bugs.
Quick: Is CustomPaint the only way to draw custom graphics in Flutter? Commit yes or no.
Common Belief:CustomPaint is the only way to draw custom graphics in Flutter.
Tap to reveal reality
Reality:Flutter also supports other methods like using widgets with decoration, ClipPath, or third-party libraries.
Why it matters:Knowing alternatives helps choose the best tool for the task.
Expert Zone
1
CustomPainter's paint method can be called many times per second; avoid heavy computations inside it to keep performance smooth.
2
The shouldRepaint method can compare complex state objects to decide repainting, but deep comparisons can hurt performance if not optimized.
3
You can combine multiple CustomPainters by layering CustomPaint widgets or using a CompositePainter pattern for modular drawing.
When NOT to use
Avoid CustomPaint for simple UI elements that standard widgets can handle, like buttons or text. For complex animations, consider using Flutter's built-in animation widgets or game engines like Flame. If you need pixel-level image manipulation, use specialized libraries instead.
Production Patterns
In production, CustomPaint is often used for charts, graphs, custom sliders, and game graphics. Developers separate drawing logic into reusable painters and optimize repaint triggers. Animations combine CustomPaint with AnimationController and Tween for smooth effects. Testing includes snapshot tests of painted output.
Connections
Canvas API (Web Graphics)
CustomPaint's Canvas API is similar to HTML5 Canvas drawing commands.
Understanding web canvas drawing helps grasp Flutter's painting commands since both use a procedural drawing model.
Declarative UI Frameworks
CustomPaint fits into Flutter's declarative widget tree but uses imperative drawing inside paint.
Knowing the mix of declarative layout and imperative drawing clarifies how Flutter balances flexibility and performance.
Traditional Painting and Art
CustomPaint mimics a painter's canvas where each stroke is deliberate and controlled.
Seeing CustomPaint as a digital painter's canvas helps appreciate the creative control it offers beyond standard UI.
Common Pitfalls
#1Drawing heavy computations inside paint causing slow UI.
Wrong approach:void paint(Canvas canvas, Size size) { for (int i = 0; i < 10000; i++) { // complex math here canvas.drawCircle(Offset(i.toDouble(), i.toDouble()), 1, paint); } }
Correct approach:Precompute data outside paint and store it; paint only draws precomputed results. void paint(Canvas canvas, Size size) { for (final point in precomputedPoints) { canvas.drawCircle(point, 1, paint); } }
Root cause:Misunderstanding that paint is called often and should be fast.
#2Always returning true from shouldRepaint causing unnecessary redraws.
Wrong approach:bool shouldRepaint(CustomPainter oldDelegate) => true;
Correct approach:bool shouldRepaint(CustomPainter oldDelegate) { return oldDelegate.someValue != this.someValue; }
Root cause:Not optimizing repaint logic leads to performance issues.
#3Trying to call setState inside CustomPainter to update drawings.
Wrong approach:class MyPainter extends CustomPainter { void update() { setState(() {}); // wrong } }
Correct approach:Manage state in a StatefulWidget and pass updated data to CustomPainter. class MyWidget extends StatefulWidget { ... } class _MyWidgetState extends State { double value = 0; void update() { setState(() { value += 1; }); } }
Root cause:Confusing widget lifecycle with painter class responsibilities.
Key Takeaways
CustomPaint is a Flutter widget that lets you draw anything by writing code in a CustomPainter.
The paint method is where you use a Canvas to draw shapes, lines, and images with full control.
The shouldRepaint method controls when Flutter redraws your painting to keep apps efficient.
CustomPaint works best combined with animations and state management for dynamic graphics.
Understanding CustomPaint bridges Flutter's declarative UI with low-level drawing power.

Practice

(1/5)
1. What is the main purpose of a CustomPainter in Flutter?
easy
A. To draw custom graphics directly on the screen
B. To manage app navigation between screens
C. To handle user input events like taps and swipes
D. To create standard UI buttons and text fields

Solution

  1. Step 1: Understand CustomPainter role

    A CustomPainter is used to draw graphics directly on the canvas in Flutter apps.
  2. Step 2: Compare with other options

    Options A, B, and C relate to standard UI, navigation, and input events, not drawing.
  3. Final Answer:

    To draw custom graphics directly on the screen -> Option A
  4. Quick Check:

    CustomPainter = Draw graphics [OK]
Hint: CustomPainter is for drawing, not UI controls or navigation [OK]
Common Mistakes:
  • Confusing CustomPainter with widget layout
  • Thinking it handles user input
  • Assuming it manages navigation
2. Which method must you override when creating a CustomPainter subclass to draw on the canvas?
easy
A. paint
B. build
C. createState
D. initState

Solution

  1. Step 1: Identify CustomPainter methods

    The paint method is where drawing commands are written.
  2. Step 2: Exclude unrelated methods

    build, createState, and initState belong to widgets, not painters.
  3. Final Answer:

    paint -> Option A
  4. Quick Check:

    Draw in paint() method [OK]
Hint: paint() draws; build() is for widgets [OK]
Common Mistakes:
  • Using build() instead of paint()
  • Confusing widget lifecycle methods with painter methods
  • Forgetting to override paint()
3. What will be the visible result of this Flutter code snippet?
class MyPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()..color = Colors.red;
    canvas.drawCircle(Offset(size.width / 2, size.height / 2), 50, paint);
  }

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

Widget build(BuildContext context) {
  return CustomPaint(
    size: Size(200, 200),
    painter: MyPainter(),
  );
}
medium
A. A blue square filling the 200x200 area
B. A red circle centered in a 200x200 area
C. Nothing is drawn because shouldRepaint returns false
D. A red circle at the top-left corner

Solution

  1. Step 1: Analyze paint method

    The paint method draws a red circle at the center (width/2, height/2) with radius 50.
  2. Step 2: Understand shouldRepaint effect

    shouldRepaint returning false means no redraw needed, but initial paint still happens.
  3. Final Answer:

    A red circle centered in a 200x200 area -> Option B
  4. Quick Check:

    Draw circle center = red circle visible [OK]
Hint: shouldRepaint false still draws initially [OK]
Common Mistakes:
  • Thinking shouldRepaint false means no drawing
  • Assuming circle is at top-left
  • Confusing circle with square shape
4. Identify the error in this CustomPainter subclass:
class MyPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()..color = Colors.green;
    canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return true
  }
}
medium
A. Canvas cannot draw rectangles
B. paint method should return a value
C. Missing semicolon after return true
D. shouldRepaint must return false

Solution

  1. Step 1: Check syntax in shouldRepaint

    The return statement lacks a semicolon after true, causing a syntax error.
  2. Step 2: Verify other parts

    paint method correctly draws a rectangle; canvas supports rectangles; shouldRepaint can return true.
  3. Final Answer:

    Missing semicolon after return true -> Option C
  4. Quick Check:

    Syntax error = missing semicolon [OK]
Hint: Check semicolons after return statements [OK]
Common Mistakes:
  • Forgetting semicolon after return
  • Thinking paint must return a value
  • Assuming canvas can't draw rectangles
5. You want to create a CustomPainter that draws a circle only when the color changes. Which is the best way to implement shouldRepaint to optimize performance?
class CirclePainter extends CustomPainter {
  final Color color;
  CirclePainter(this.color);

  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()..color = color;
    canvas.drawCircle(Offset(size.width/2, size.height/2), 40, paint);
  }

  @override
  bool shouldRepaint(covariant CirclePainter oldDelegate) {
    // What to write here?
  }
}
hard
A. return oldDelegate.color == color;
B. return true;
C. return false;
D. return oldDelegate.color != color;

Solution

  1. Step 1: Understand shouldRepaint purpose

    shouldRepaint returns true if the painter needs to redraw due to changes.
  2. Step 2: Compare colors to decide repaint

    Repaint only if the new color differs from the old one, so compare oldDelegate.color and color.
  3. Step 3: Evaluate options

    return oldDelegate.color != color; returns true when colors differ, optimizing redraw. return true; always repaints (less efficient). return false; never repaints (ignores changes). return oldDelegate.color == color; repaints when colors are same (wrong logic).
  4. Final Answer:

    return oldDelegate.color != color; -> Option D
  5. Quick Check:

    Repaint if color changed = oldDelegate.color != color [OK]
Hint: Repaint only if properties differ for efficiency [OK]
Common Mistakes:
  • Always returning true wastes performance
  • Always returning false ignores changes
  • Using equality check wrongly in shouldRepaint
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard