In Flutter, how do ComponentElement and RenderObjectElement differ, and which kinds of widget create each one?
answer
- builds a child vs owns a render object
- Stateless, Stateful, Proxy elements
- Leaf, SingleChild, MultiChild widgets
- updateRenderObject on update
- render tree skips component nodes
basics
~20 sA 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.
solid answer
~40 s**`ComponentElement`** is the composing kind: its job is to call `build` (or return its proxy child) and manage the single child element that results. `StatelessElement`, `StatefulElement` and `ProxyElement` (used by `InheritedWidget` and `ParentDataWidget`) are its subclasses, so `Container`, `Text` and your own widgets land here, and none of them owns a render object. **`RenderObjectElement`** owns a render object: on mount it calls `createRenderObject`, on update `updateRenderObject`, and it inserts, moves and removes child render objects as child elements change. Its widget side comes in three shapes: `LeafRenderObjectWidget` (no children, such as `RawImage`), `SingleChildRenderObjectWidget` (one `child`, such as `Padding` or `ColoredBox`) and `MultiChildRenderObjectWidget` (a `children` list, such as `Flex` and so `Column` and `Row`). Only the second kind appears in the render tree.
go deeper
Recall that composing widgets build other widgets, while a smaller set of widgets actually create render objects.
Name the ComponentElement subclasses and the three RenderObjectWidget base classes with examples, and explain why the render tree is smaller.
Pick the right RenderObjectWidget base for a custom layout and explain how child render objects are attached through the nearest render object ancestor.
Judge when the extra rendering-layer code is justified versus composing existing widgets, given the maintenance cost for the team.
## Two families of element Every element is one of two broad kinds, and the kind decides whether it shows up in the render tree. | | `ComponentElement` | `RenderObjectElement` | |---|---|---| | Created by | `StatelessWidget`, `StatefulWidget`, `ProxyWidget` (incl. `InheritedWidget`) | `RenderObjectWidget` subclasses | | Owns a render object | No | Yes, exactly one | | On mount | Builds its child widget and inflates it | Calls `createRenderObject`, attaches it, inflates children | | On update | Rebuilds, producing a new child widget | Calls `updateRenderObject`, then updates children | | Children | Always a single child element | Zero, one or many, depending on the widget kind | ## ComponentElement: composition A **`ComponentElement`** exists to turn one widget into another. Its `performRebuild` calls `build()` and hands the result to `updateChild`: - a **`StatelessElement`** calls `widget.build(this)`; - a **`StatefulElement`** calls `state.build(this)` on the `State` it owns; - a **`ProxyElement`** does not build anything new: it returns the `child` its widget was given. `InheritedElement` and `ParentDataElement` are proxies, which is why an `InheritedWidget` adds a node to the element tree but none to the render tree. Because these elements own no render object, when they need to put something on screen they delegate: their descendant eventually reaches a `RenderObjectWidget`, and that element attaches its render object to the nearest ancestor `RenderObjectElement`'s render object. ## RenderObjectElement: the bridge to layout and paint A **`RenderObjectElement`** keeps a render object in sync with its widget. It calls `createRenderObject(context)` once when mounted and `updateRenderObject(context, renderObject)` on each update, and it manages how child render objects are inserted, moved and removed as its child elements change. The framework provides three concrete element types, each paired with a widget base class: 1. **`LeafRenderObjectWidget`** → `LeafRenderObjectElement`: no child widgets. Examples: `RawImage`, `ErrorWidget`. 2. **`SingleChildRenderObjectWidget`** → `SingleChildRenderObjectElement`: one optional `child`. Examples: `Padding`, `SizedBox`, `ColoredBox`, `Opacity`, `Align`. 3. **`MultiChildRenderObjectWidget`** → `MultiChildRenderObjectElement`: a `children` list, reconciled in linear time per list. Examples: `Flex` (and so `Row` and `Column`), `Stack`, `Wrap`, `RichText`. The render object on the other side must support that child model: a single-child render object holds one child directly, while a multi-child one keeps a child list. ## Why the render tree is smaller Consider one crossword cell built as `Container(color: ..., padding: ..., child: Text(letter))`: - `Container` → `StatelessElement` (component, no render object) - it builds `ColoredBox` → `SingleChildRenderObjectElement` → a colored-box render object - inside that, `Padding` → single-child element → `RenderPadding` - `Text` → `StatelessElement` (component) - it normally builds `RichText` → `MultiChildRenderObjectElement` → `RenderParagraph` Five elements, three render objects. Across a board of hundreds of cells, layout and paint walk only the render objects, skipping every component node. ## How a child finds its render parent Because component elements have no render object, a render object element cannot simply attach to its element parent. When it mounts, `attachRenderObject` walks up to the **nearest ancestor `RenderObjectElement`** and asks it to insert the new render object at the given slot. That ancestor's element knows its child model: - a single-child element sets its render object's one child; - a multi-child element inserts the child into the list after the previous sibling; - when a child moves, its slot changes and the ancestor moves the render object rather than recreating it. `ParentDataWidget`s such as `Expanded` or `Positioned` are proxy elements too: they add no render object, but they write layout information into the child render object's `parentData` so that the `RenderFlex` or `RenderStack` above can use it. ## Picking a base class when you write one When you write a `RenderObjectWidget`, choose the base class by the number of children the render object needs: - no children (a custom gauge or a single drawn glyph): `LeafRenderObjectWidget`; - wraps one child (a custom clipping or offset box): `SingleChildRenderObjectWidget`; - lays out many children (a custom grid like a crossword board): `MultiChildRenderObjectWidget`. For children in named slots rather than a list, the framework has a slotted variant as well. Writing the render object itself (its layout, paint and hit testing) is a separate topic; the element kind only decides how children and the render object are wired together.
- An InheritedWidget sits between a Padding and its child. Where does it appear in each tree?It has an element, an `InheritedElement`, which is a `ProxyElement` and therefore a `ComponentElement`. It owns no render object, so in the render tree the `RenderPadding`'s child is whatever render object the inherited widget's descendants produce.
- Which base class would you pick for a custom widget that arranges a list of child widgets in a crossword layout?`MultiChildRenderObjectWidget`. It stores the `children` list, and its `MultiChildRenderObjectElement` reconciles child elements and inserts, moves and removes their render objects in your render object's child list. `Leaf` has no children and `SingleChild` holds exactly one.
saying these in an interview costs you the question
- StatelessWidget creates a render object that paints its build output.
- InheritedWidget adds a node to the render tree.
- A SingleChildRenderObjectWidget can hold a list of children.
- ComponentElement and RenderObjectElement are two names for the same class.
- Every element has a render object, possibly an empty one.