skip to content

Engine & Frame Pipeline

What sits beneath the widgets: the element and render-object trees, the build-layout-paint frame, the Impeller renderer and canvas painting. Interviewers use it to test why a frame costs what it does.

part ofFlutteroverview, primer and where to startread it →
on this pageshow

explore

questions

25

In Flutter, what are the widget, element and render object trees, and what does each one hold?

level: juniorimportance: must knowfreq 72%

answer

  1. configuration, instance, geometry
  2. widgets are immutable and short-lived
  3. elements keep position, State and children
  4. render objects lay out, paint, hit-test
  5. render tree is a subset

basics

~20 s

Widgets are immutable, short-lived descriptions of UI. Elements are the long-lived instances that hold each widget's position in the tree, its children and any State. Render objects, created by RenderObjectWidgets, hold size and position and do layout, painting and hit testing.

solid answer

~40 s

The **widget tree** is what your `build` methods return: immutable configuration objects, recreated on every rebuild. For each widget the framework creates an **element** via `createElement()`, and the element tree is the long-lived structure: each element knows its parent, its children, its slot, the widget currently configuring it and, for a `StatefulWidget`, its `State` object. Only widgets that extend `RenderObjectWidget` (such as `Padding`, `ColoredBox` or `Flex`) produce a **render object**; the render tree stores geometry and does layout, painting and hit testing. Because composing widgets like `Container`, `Text` or your own `StatelessWidget`s have no render object of their own, the render tree is a subset of the element tree. Rebuilding creates new widgets, while elements and render objects are reused and updated in place.

go deeper

for a junior

Recall the three layers in one line each: immutable widget configuration, long-lived elements holding position and State, render objects doing layout and paint.

for a middle

Explain createElement and createRenderObject, why the render tree is a subset, and how element reuse lets State survive rebuilds.

for a senior

Use the model to reason about bugs: lost state when an element is replaced, and wasted layout when a subtree's render objects are recreated.

for a principal

Relate the split to how a team structures widgets: cheap composition at the widget layer, and custom render objects only where profiling justifies them.

## Three trees, three jobs Flutter separates *what the UI should look like* from *the objects that exist on screen*. The split is into three parallel structures: | Tree | Made of | Lifetime | Job | |---|---|---|---| | Widget | `Widget` subclasses | One build; recreated on every rebuild | Immutable configuration | | Element | `Element` subclasses | As long as that spot in the UI persists | Identity, position, children, `State`, dependencies | | Render | `RenderObject` subclasses (usually `RenderBox`) | As long as its element | Layout, paint, hit testing | ## Widgets: immutable configuration A **widget** is a lightweight object annotated `@immutable`: all fields are `final`. It does not know its parent or children in the live UI and cannot remember anything between frames. Writing `Padding(padding: EdgeInsets.all(8), child: Text('A'))` just allocates two small objects describing intent. `build` methods return new widget objects every time they run. ## Elements: the living instance tree Every widget has a `createElement()` method. When a widget is first placed in the tree, the framework **inflates** it into an **element** and mounts that element under its parent. The element tree is the real, persistent structure of the app: - it records each element's **parent**, **children** and **slot** (its position among siblings); - it points at the **current widget** configuring it, swapped for a newer one on rebuild; - a `StatefulElement` owns the **`State`** object, which is why state survives rebuilds; - it tracks **dirtiness** and **inherited dependencies**, so the framework rebuilds only what changed. The `BuildContext` passed to `build` is in fact the element, which is why looking things up through a context means looking at the element's ancestors. ## Render objects: geometry and pixels Only widgets that extend **`RenderObjectWidget`** create a render object, through `createRenderObject(context)`. A `RenderBox` receives constraints from its parent, chooses a size, positions its children, paints into a layer and answers hit tests. The render tree is where the frame's layout and paint phases run. Most widgets you write compose other widgets and never touch a render object. So the three trees are not the same size: 1. The widget and element trees correspond one-to-one: each mounted element has exactly one current widget. 2. The render tree is a **subset**: `Container`, `Text`, `ElevatedButton` and your own `StatelessWidget`s have elements but no render object of their own; their descendants eventually reach `RenderObjectWidget`s such as `Padding`, `DecoratedBox` or `RichText`. ## Why split them at all Flutter's own architecture notes give the reasons: - **Cheap rebuilds.** Widgets can be thrown away every frame because the expensive, stateful parts live in elements and render objects that are reused. - **Faster layout.** Layout walks only the render tree, which skips the many purely compositional nodes of the element tree. - **Clear protocols.** The widget API is about configuration; the render object API is about constraints, sizes and painting, and each can be specialised without the other. ## Misconceptions worth correcting - **"A widget is a view."** A widget never draws anything; it is a description. The object that paints is a render object, and many widgets never produce one. - **"setState rebuilds the render tree."** `setState` marks an element dirty so its `build` runs again. Render objects are updated only where a render object widget receives changed values. - **"State belongs to the widget."** The `State` object is owned by the element. The widget is the configuration the `State` currently reads through its `widget` getter, and that reference changes on each rebuild. - **"The three trees are the same shape."** Widgets and elements correspond one-to-one, but the render tree is thinner, because composition adds elements without render objects. ## Walking through a crossword cell A crossword board shows a cell as `Container(color: ..., child: Text(letter))`: - **Widgets:** a `Container` and a `Text`, recreated whenever the board rebuilds. - **Elements:** a `StatelessElement` for `Container`, which builds a `ColoredBox` element, and elements for `Text` and the `RichText` it builds. - **Render objects:** a colored-box render object and a `RenderParagraph`; the `Container` and `Text` elements themselves own none. When the player types a letter, new `Container` and `Text` widgets are built, the existing elements are handed the new widgets, and the `RenderParagraph` is told its text changed. Nothing is torn down. Keys, the rule that decides whether an element can take a new widget, and the frame phases that follow are covered in neighbouring topics.

  • Where does a StatefulWidget's State object live, and why does that let it survive a rebuild?
    In the `StatefulElement`, which calls `createState()` once when it is created and keeps the `State` for its whole life. A rebuild produces a new widget instance, but if the element can take it, the element keeps its `State` and just points `state.widget` at the new configuration.
  • Does every widget in a Flutter tree have a render object?
    No. Only `RenderObjectWidget` subclasses create one. Composing widgets such as `Container`, `Text` or your own `StatelessWidget`s produce elements whose job is to build other widgets; the render tree contains only the render objects their descendants create, so it is a subset of the element tree.

Widgets are the architect's blueprints, redrawn freely; elements are the site crew who keep track of which room is which and who lives there; render objects are the physical walls that get measured and painted.

saying these in an interview costs you the question

  • Every widget creates its own render object.
  • Widgets hold mutable state that survives between builds.
  • The State object lives inside the StatefulWidget instance.
  • Rebuilding a widget recreates its element and render object.
  • The element tree is only a debugging view of the widgets.
open as a page

In Flutter, when is CustomPainter.shouldRepaint called, what should it return, and what goes wrong with always true or always false?

level: middleimportance: must knowfreq 50%

basics

~20 s

shouldRepaint runs when a new painter instance replaces the old one, typically after a rebuild. Return true only if the new painter would draw something different, by comparing its fields with oldDelegate's. Always true wastes repaints; always false leaves stale drawings.

open as a page

In a Flutter voice-memo app, how do you repaint a live audio waveform on every new amplitude sample without rebuilding the widget tree?

level: middleimportance: must knowfreq 45%

basics

~10 s

Keep the samples in a ChangeNotifier owned by the State and pass it to the painter's super(repaint: ...). Each notifyListeners makes RenderCustomPaint call markNeedsPaint, so only paint runs: no setState, no build, no layout.

open as a page

In Flutter, what happens between one vsync signal and the next rendered frame, and in what order do the phases run?

level: middleimportance: must knowfreq 50%

basics

~20 s

Each frame, SchedulerBinding runs transient callbacks (animation ticks), then their microtasks, then persistent callbacks that rebuild dirty elements, lay out, paint into a layer tree and composite it, then post-frame callbacks. The raster thread draws the resulting scene.

open as a page

In Flutter, what work runs on the UI thread versus the raster thread, and what changed when the UI and platform threads were merged?

level: middleimportance: must knowfreq 50%

basics

~20 s

The UI thread runs Dart (build, layout, paint) and produces a layer tree; the raster thread renders it through the GPU. Since Flutter 3.29 on iOS and Android, Dart runs on the platform main thread; rasterizing stays separate.

open as a page

What is Flutter's Impeller renderer, and what problem made it replace Skia as the default on iOS and Android?

level: middleimportance: must knowfreq 55%

basics

~20 s

Impeller is the engine's rendering runtime. Skia compiled shaders just in time, so an effect's first use could stall a frame; Impeller precompiles a smaller shader set when the engine is built and creates pipelines upfront, ending that first-run jank.

open as a page

In Flutter, why is it cheap for a crossword board of about 500 widgets to run build() again after the player types one letter?

level: middleimportance: must knowfreq 64%

basics

~20 s

Rebuilding only allocates small immutable widget objects. The long-lived elements are reused and handed the new widgets; an identical widget stops the walk, and render object setters mark layout or paint only for values that actually changed.

open as a page

In Flutter, how do the CustomPaint widget and a CustomPainter work together to draw custom graphics, and what must a CustomPainter implement?

level: juniorimportance: should knowfreq 45%

basics

~20 s

CustomPaint is the widget that sizes and hosts the drawing; a CustomPainter is the delegate that draws. A painter implements paint(Canvas, Size) to draw inside that size, and shouldRepaint(oldDelegate) to say whether a new instance needs a repaint.

open as a page

In a Flutter comments list, why does scrolling to maxScrollExtent right after setState stop short of the new comment, and how do you fix it?

level: juniorimportance: should knowfreq 45%

basics

~20 s

setState only marks the widget dirty; the list is rebuilt and laid out in the next frame, so maxScrollExtent read immediately still describes the old list. Scroll inside WidgetsBinding.instance.addPostFrameCallback, which runs after that frame's layout.

open as a page

In Flutter's architecture, what do the framework, the engine and the platform embedder each do, and how does a frame cross them?

level: juniorimportance: should knowfreq 55%

basics

~20 s

The framework (Dart) turns widgets into a layer tree; the engine (C++) runs Dart, exposes dart:ui and rasterizes scenes with Impeller; the platform embedder hosts the engine in a native app, providing the surface, threads, input and lifecycle.

open as a page

In Flutter, why might a CustomPaint draw nothing or spill outside its area, and how do size, clipping and save/restore fix it?

level: middleimportance: should knowfreq 35%

basics

~20 s

Without a child, CustomPaint asks for its size argument, Size.zero by default, so loose constraints give the painter an empty box. Drawing outside size is not reliably clipped, so clip to Offset.zero & size, and keep save and restore balanced.

open as a page

In Flutter 3.47, which graphics backend does the renderer use on each platform, and where does Skia still render?

level: middleimportance: should knowfreq 35%

basics

~10 s

iOS and macOS: Impeller on Metal. Android API 29+: Impeller on Vulkan, else Impeller's OpenGL ES backend; below API 29, Skia on OpenGL ES. Windows and Linux: Impeller since 3.47, over OpenGL. Web: Skia.

open as a page

In Flutter, what does markNeedsLayout cost compared with markNeedsPaint on a RenderObject, and how do you choose between them in a setter?

level: middleimportance: should knowfreq 40%

basics

~20 s

markNeedsLayout schedules layout and then paint, and can spread up to ancestors whose layout depends on this size. markNeedsPaint only repaints up to the nearest repaint boundary. Call markNeedsLayout when size or child positions change, markNeedsPaint when only pixels change.

open as a page

In Flutter, how do ComponentElement and RenderObjectElement differ, and which kinds of widget create each one?

level: middleimportance: should knowfreq 32%

basics

~20 s

A ComponentElement builds one child widget and owns no render object; stateless, stateful and inherited widgets create one. A RenderObjectElement owns a render object and keeps it in sync; Leaf, SingleChild and MultiChild RenderObjectWidgets create one.

open as a page

In Flutter, what happens to an Element during mount, update, deactivate and unmount, and when does each step occur?

level: middleimportance: should knowfreq 38%

basics

~20 s

An element is created and mounted when its widget first appears, updated in place when a compatible new widget arrives, deactivated into an inactive list when removed, and unmounted at the end of the frame unless something reclaims it.

open as a page

In Flutter, a callback registered with addPostFrameCallback from a stream listener sometimes fires seconds late; why, and how do you make it reliable?

level: seniorimportance: should knowfreq 25%

basics

~10 s

addPostFrameCallback does not request a frame. On an idle screen, the callback waits until something else, such as a touch or an animation, schedules one. Request a frame too, with SchedulerBinding.instance.ensureVisualUpdate(), or await SchedulerBinding.instance.endOfFrame.

open as a page

In Flutter, what does a RepaintBoundary change in the paint phase, and when does adding one help or hurt performance?

level: seniorimportance: should knowfreq 40%

basics

~20 s

RepaintBoundary gives its subtree its own layer: a repaint inside no longer repaints ancestors, and an ancestor repaint reuses its recorded content. It helps where parent and child repaint at different times and only adds cost where they repaint together.

open as a page

Users of a Flutter meditation app report that the breathing animation stutters only the first time after install; how do you decide whether Impeller should have prevented it?

level: seniorimportance: should knowfreq 35%

basics

~20 s

First-run-only stutter is the signature of just-in-time shader compilation, which Impeller removes. So check which renderer the affected devices use (Android below API 29 still runs Skia); on Impeller devices, hunt other one-time costs such as image decoding.

open as a page

In a custom Flutter RenderBox, what must performLayout do, and when should you set sizedByParent and implement computeDryLayout?

level: seniorimportance: should knowfreq 24%

basics

~20 s

performLayout lays out children, positions them via parentData and sets a size within the constraints. Set sizedByParent only when size depends on constraints alone. Implement computeDryLayout whenever a parent may ask your size without laying you out; it must match performLayout.

open as a page

In Flutter, when is writing a custom RenderBox worth it instead of composing existing widgets or drawing with CustomPaint?

level: seniorimportance: should knowfreq 28%

basics

~10 s

Write a custom RenderBox only when composition, CustomPaint and layout delegates cannot express the layout, hit testing or performance you need. You then own layout, painting, hit testing, dry layout, intrinsics and semantics yourself.

open as a page

When you write a Flutter SingleChildRenderObjectWidget, what must createRenderObject and updateRenderObject each do, and what breaks if updateRenderObject is left empty?

level: seniorimportance: should knowfreq 26%

basics

~20 s

createRenderObject builds the render object once, from every widget field and any context values. updateRenderObject must copy the same fields onto the existing object on every update; left empty, the UI keeps showing the first configuration after rebuilds.

open as a page

In Flutter, what are the three jobs of a custom RenderBox, and which method does it override for each?

level: juniorimportance: nice to knowfreq 22%

basics

~10 s

A RenderBox lays out, paints and hit-tests. It sets its size from the parent's BoxConstraints in performLayout, draws in paint(PaintingContext, Offset), and answers pointer hits through hitTestSelf and hitTestChildren.

open as a page

In Flutter, how do you use a custom GLSL fragment shader in a CustomPainter, from pubspec declaration to setting uniforms each frame?

level: seniorimportance: nice to knowfreq 15%

basics

~20 s

List the .frag file under flutter: shaders: in pubspec.yaml, load it once with FragmentProgram.fromAsset, create a FragmentShader, set uniforms with setFloat in declaration order, and draw with Paint()..shader = shader in paint. The tool compiles the shader at build time.

open as a page

How do you temporarily switch a Flutter app off Impeller to check a rendering bug, and why is that only a short-term measure?

level: seniorimportance: nice to knowfreq 20%

basics

~20 s

Use flutter run --no-enable-impeller for a debug session, or the per-platform build switch such as Android's EnableImpeller manifest entry. iOS has no opt-out and the others are slated for removal, so the switch only isolates the bug.

open as a page

In a custom Flutter RenderBox with children, how do parentData offsets, paint and hitTestChildren work together?

level: seniorimportance: nice to knowfreq 16%

basics

~20 s

The parent installs a parentData object on each child in setupParentData, writes each child's position into it during performLayout, paints each child at offset plus that position, and hit-tests children in reverse paint order after subtracting it.

open as a page