skip to content

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.