tezvyn:

In a CustomPainter, what is the purpose of the shouldRepaint method?

AI-drafted, machine-checkedSource: api.flutter.devintermediate

This tests Flutter repaint optimization. Answer: shouldRepaint compares old and new delegates, returning true only when visual properties differ so the framework skips paint calls. A red flag is returning true unconditionally, forcing useless repaints.

WHAT THIS TESTS: Your understanding of the Flutter rendering layer's repaint boundary optimization. The interviewer wants to know if you grasp that CustomPainter delegates can be swapped without triggering paint, and that you know how to implement value-based equality checks to short-circuit expensive drawing operations.

A GOOD ANSWER COVERS: First, the contract: shouldRepaint is called when a new delegate is provided to the RenderCustomPaint object. It receives the old delegate and must return true if the new instance carries different visual information. Second, the performance implication: returning false allows the framework to optimize away the paint call entirely. Third, the caveats: paint can still be called even if shouldRepaint returns false when an ancestor or descendant repaints, or when the box size changes. Fourth, practical implementation: compare specific fields that affect the Canvas output, such as colors, stroke widths, animation progress, or path data, rather than doing identity checks or deep equality on large objects.

COMMON WRONG ANSWERS: Always returning true because the developer assumes paint is cheap. Ignoring oldDelegate and returning false unconditionally, which causes stale frames. Performing deep equality or list comparisons inside shouldRepaint, which costs more than just painting. Confusing shouldRepaint with shouldRebuild or thinking it controls layout. Suggesting that shouldRepaint guarantees paint will not be called, which contradicts the framework's behavior around ancestor repaints and size changes.

LIKELY FOLLOW-UPS: When would paint be called even if shouldRepaint returns false? The answer is ancestor repaints, size changes, or RepaintBoundary behavior. How does a RepaintBoundary help with an expensive painter? It isolates the layer so ancestor changes do not force a repaint of this painter. What is the difference between a CustomPainter and a RenderObject? This probes whether you understand the delegate pattern versus direct render tree manipulation.

ONE CONCRETE EXAMPLE: Imagine a CircularProgressPainter that draws an arc based on a double progress value from zero to one. The painter stores progress, trackColor, and fillColor as final fields. In shouldRepaint, you cast oldDelegate to CircularProgressPainter and return oldDelegate.progress differs from progress or oldDelegate.trackColor differs from trackColor or oldDelegate.fillColor differs from fillColor. If the widget rebuilds with the same progress because the parent setState fired for an unrelated reason, shouldRepaint returns false and the framework skips paint. If the progress ticks from 0.3 to 0.31, it returns true and the arc redraws. This pattern keeps the GPU workload minimal during idle rebuilds.

Read the original → api.flutter.dev

Get five bites like this every day.

Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.