RepaintBoundary, Const Widgets, and Rebuild Pruning
Reduce unnecessary repaints and rebuilds with boundaries and const constructors.
Rebuilds vs Repaints
Flutter performance work lives at three layers, and conflating them wastes effort:
- Rebuild —
build()runs again, producing a new widget tree. Cheap if widgets are immutable and shallow, but it can cascade. - Relayout — RenderObjects recompute size/position. Triggered by constraint or child changes.
- Repaint — pixels are re-rasterized to a layer. Expensive for gradients, shadows, and complex paths.
This lesson targets two distinct wins: pruning rebuilds with const and structure, and isolating repaints with RepaintBoundary. They solve different problems — do not reach for one when you need the other.
Why const Widgets Skip Rebuilds
A const widget is canonicalized by the Dart compiler: every evaluation of the same const expression returns the identical instance. When a parent rebuilds, Flutter compares the new child widget to the old one. If they are the same instance (identical(old, new) is true), Flutter short-circuits and does not rebuild that subtree at all.
- Without
const, each parent build creates a freshWidgetobject, forcing the element to update its child. - With
const, the canonical instance is reused, so the subtree is skipped entirely.
This is the cheapest optimization in Flutter — it costs you a keyword and prunes whole branches of the rebuild.
// Dart compile-time canonicalization: const instances are identical.
class Point {
final int x;
final int y;
const Point(this.x, this.y);
}
void main() {
const a = Point(1, 2);
const b = Point(1, 2);
// Both refer to the SAME canonical instance.
print(identical(a, b)); // true
final c = Point(1, 2); // runtime instance, not canonicalized
print(identical(a, c)); // false
}All lessons in this course
- The Three Trees: Widget, Element, and RenderObject
- Profiling Jank with the DevTools Timeline
- RepaintBoundary, Const Widgets, and Rebuild Pruning
- Shader Warm-Up and Impeller Migration