Elements
What are elements?
The element tree is anchored in the
WidgetsBinding
and established viarunApp
/RenderObjectToWidgetAdapter
. Widgets are immutable representations of UI configuration data. Widgets are “inflated” intoElement
instances, which serve as their mutable counterparts. Among other things, elements model the relationship between widgets (e.g., the widget tree), store state / inherited relationships, and participate in the build process.Many lifecycle events are triggered by changes to the element tree. In particular, all elements are associated with a
BuildOwner
that is responsible for tracking dirty elements and, duringWidgetsBinding.drawFrame
, re-building the widget / element tree. This drives lifecycle events within widget and state objects.Elements are initially mounted. They may then be updated multiple times. An element may be deactivated (by the parent via
Element.deactivateChild
); it can be activated within the same frame, else it will be unmounted.Element.updateChild
is used to alter the configuration (widget) of a given child, potentially inflating a new element if none exists, the new and old widgets do not have the same type, or the widgets have different keys. Updating an element may update the element’s children.
What are the element building blocks?
Elements are broken down into
RenderObjectElements
andComponentElements
.RenderObjectElements
are responsible for configuring render objects and keeping the render object tree and widget tree in sync.ComponentElements
do not directly manage render objects but instead produce intermediate nodes via mechanisms likeWidget.build
. Both are triggered byElement.performRebuild
which is itself triggered byBuildOwner.buildScope
.
How is the render tree managed by RenderObjectElement
?
RenderObjectElement
?Render object elements are responsible for managing an associated render object.
RenderObjectElement.update
will cause the associated render object to be updated to match a new configuration (widget). This render object may have children; however, there may be several intermediate elements between its render object element and any descendent render object elements (due to intervening component elements). Slot tokens are passed down the element tree so that the render object elements corresponding to these children can integrate with the parent render object. This is managed viaRenderObjectElement.insertChildRenderObject
,RenderObjectElement.moveChildRenderObject
, andRenderObjectElement.removeChildRenderObject
.Elements can be moved during a frame. Such elements are “forgotten” so that they are excluding from iteration / updates, with the actual mutation taking place when the element is added to its new parent.
When updating, children must be updated, too. To avoid unnecessarily inflation (and potential loss of state), the new and old child lists are synchronized using a linear reconciliation scheme optimized for empty lists, matched lists, and lists with one mismatched region:
The leading elements and widgets are matched by key and updated
The trailing elements and widgets are matched by key with updates queued (update order is significant)
A mismatched region is identified in the old and new lists
Old elements are indexed by key
Old elements without a key are updated with null (deleted)
The index is consulted for each new, mismatched widget
New widgets with keys in the index update together (re-use)
New widgets without matches are updated with null (inflated)
Remaining elements in the index are updated with null (deleted)
What are the render object element building blocks?
LeafRenderObjectElement
,SingleChildRenderObjectElement
, andMultiChildRenderObjectElement
provide support for common use cases and correspond to the similarly named widget helpers. The multi-child and single-child variants integrate withContainerRenderObjectMixin
andRenderObjectWithChildMixin
, respectively.These use the previous child (or null) as the slot identifier; this integrates nicely with
ContainerRenderObjectMixin
which supports an “after” argument when managing children.
How are components managed by ComponentElement
?
ComponentElement
?ComponentElement.build
provides a hook for producing intermediate nodes in the element tree.StatelessElement.build
invokes the widget’s build method, whereasStatefulElement.build
invokes the state’s build method. Mounting and updating cause rebuild to be invoked. ForStatefulElement
, a rebuild may be scheduled spontaneously viaState.setState
. In both cases, lifecycle methods are invoked in response to changes to the element tree (for example,StatefulElement.update
will invokeState.didUpdateWidget
).If a component element rebuilds, the old element and new widget will still be paired, if possible. This allows
Element.update
to be used instead ofElement.inflateWidget
. Consequently, descendant render objects may be updated instead of recreated. Provided that the render object’s properties weren’t changed, this will likely circumvent the rest of the rendering process.
How do element dependencies work (inheritance)?
Elements are automatically constructed with a hashmap of ancestor inherited widgets; thus, if a dependency is eventually added (via
Element.inheritFromWidgetOfExactType
), it is not necessary to walk up the tree. When elements express a dependency, that dependency is added to the element’s map viaElement.inheritFromElement
and communicated to the inherited ancestor viaInheritedElement.updateDependency
.Locating ancestor render objects / widgets / states that do not utilize
InheritedElement
requires a linear walk up the tree.InheritedElement
is a subclass ofProxyElement
, which introduces a notification mechanism so that descendents can be notified afterProxyElement.update
(viaProxyElement.updated
andProxyElement.notifyClients
). This is useful for notifying dependant elements when dependencies change so that they can be marked dirty.
Last updated