RepaintBoundary class
Marks child's subtree as its own paint isolate.
On frames where nothing inside the boundary has changed, the framework composites the boundary's cached cells into the frame buffer instead of walking the subtree's paint methods — one pass over the cached cells instead of a recursive paint chain. Cells the subtree left empty show what lies beneath, as they would painted directly.
The cache covers the child's layout size. Content outside that rectangle is clipped, including its focus and semantic bounds. Give overflowing content enough layout space, or place it in an Overlay outside the boundary instead.
Wrap a subtree whose paint you want isolated from a neighbour's churn (a
sidebar beside a live log, a static header above an animating body). Direct
use is opt-in, like Flutter's RepaintBoundary.
One place inserts them automatically: ListView wraps each item (its
addRepaintBoundaries flag, on by default), because a list is the canonical
"one row changes, the rest don't" shape. Measured on the paint-walk probe, a
localized update in an N-row list is ~3x faster even for trivially cheap
rows and 6-16x for styled full-width rows — the paint walk over N rows
costs more than blitting N-1 cached rows regardless of per-row cost. The
trade is one reused cache buffer per visible item (bounded by the viewport),
which for a TUI is a small, fixed memory cost for a large CPU win.
The boundary's child paints into the cache the first time, and again
whenever a RenderObjectElement inside it is reconciled or any render
object's child list changes. Stable subtrees (const widgets, or
instances kept identical across builds) stay cached across many frames.
A list row that merely SCROLLS keeps its content and only changes offset, so
it cache-hits and blits at the new row — the pruning survives scrolling.
The one anti-pattern: do NOT combine boundaries with KEYS to "recycle" rows of changing content (the keyed-list pattern from DOM frameworks). Keyed boundaries make every moved-and-mutated row a reconciled subtree — each invalidates its cache, so you pay the boundary bookkeeping AND the repaint (measured ~2x slower), and it defeats scroll detection (which turns shifted rows into a single buffer move). ListView's per-item boundaries are deliberately POSITIONAL (unkeyed), which is exactly why they cache-hit on scroll instead of thrashing.
- Inheritance
-
- Object
- Widget
- RenderObjectWidget
- SingleChildRenderObjectWidget
- RepaintBoundary
- Available extensions
Constructors
- RepaintBoundary({Key? key, Widget? child})
-
const
Properties
Methods
-
animate(
{Duration? duration, Curve? curve, Object? trigger, bool repeat = false}) → Animate -
Available on Widget, provided by the AnimateExtension extension
Starts an Animate chain over this widget.duration/curveapply to the whole chain. With notriggerit plays on mount; with a non-null trigger it rests on mount and replays whenever the token changes. -
createElement(
) → SingleChildRenderObjectElement -
Creates the Element that will mount this widget into the tree.
inherited
-
createRenderObject(
BuildContext context) → RenderRepaintBoundary -
Creates the render object backing this widget. Called once when the
element first mounts.
override
-
hasEquivalentWidgetConfiguration(
Widget other) → bool -
noSuchMethod(
Invocation invocation) → dynamic -
Invoked when a nonexistent method or property is accessed.
inherited
-
toString(
) → String -
A string representation of this object.
inherited
-
updateRenderObject(
BuildContext context, covariant RenderObject renderObject) → void -
Applies any changes from a new widget instance to the existing
render object. Default is a no-op; subclasses override when the
widget carries layout/paint configuration.
inherited
Operators
-
operator ==(
Object other) → bool -
The equality operator.
inherited