WidgetsBindingand established via
Widgetinstances are immutable representations of UI configuration data that are “inflated” into
Element.inflateWidget). Elements therefore serve as widgets' mutable counterparts and are responsible for modeling the relationship between widgets (e.g., the widget tree), storing state and inherited relationships, and participating in the build process, etc.
BuildOwnersingleton. This instance is responsible for tracking dirty elements and, during
WidgetsBinding.drawFrame, re-building the element tree as needed. This process triggers several lifecycle events (e.g.,
Element.unmount). Whereas these operations are permanent, elements may also be temporarily removed and restored (via
_ElementLifecycle) in response to the following methods:
Element.activateis only called when reactivating),
inactive, can be reactivated via
Element.activate), then finally
BuildOwner._inactiveElements). All descendant elements are affected (via
Element.update) multiple times as they become dirty (e.g., due to widget changes or notifications). An element may also be deactivated; this removes any associated render objects from the render tree and adds the element to the build owner's list of inactive nodes. This list (
_InactiveElements) automatically deactivates all nodes in the affected subtree and clears all dependencies (e.g., from
Element.deactivateChild). Deactivation temporarily removes the element (and any associated render objects) from the element tree; unmounting makes this change permanent.
Element.updateChildis used to update a child element when its configuration (i.e., widget) changes. If the new widget isn't compatible with the old one (e.g., doesn't exist, has a different type, or has a different key), a fresh element is inflated (via
Element.inflateWidget). Once an element is retrieved or inflated, the new configuration is applied via
Element.update; this might alter an associated render object, notify dependents of a state change, or mutate the element itself.
RenderObjectElementsare responsible for configuring render objects and keeping the render object tree and widget tree in sync.
ComponentElementsdon't directly manage render objects but instead produce intermediate nodes via mechanisms like
Widget.build. Both processes are driven by
Element.performRebuildwhich is itself triggered by
BuildOwner.buildScope. The latter is run as part of the build process every time the engine requests a frame.
ProxyElementforms a third category of elements that wrap a subtree of elements (and are configured by
ProxyWidget). These generally augment the subtree in some way (e.g.,
InheritedElementinjects heritable state). Proxy elements use notifications to inform subscribers when its configuration changes (
ProxyElement.updatedwhich, by default, calls
ProxyElement.notifyClients). Subclasses manage subscribers in an implementation-specific way.
ParentDataElementupdates the parent data of all closest descendant render objects (via
ParentDataElement._applyParentData, which is called by
InheritedElementnotifies a set of dependents whenever its configuration is changed (i.e., when
InheritedElement._dependantsis implemented as a mapping since each dependent can provide an arbitrary object to use when determining whether an update is applicable. Dependents are notified by invoking
RenderObjectElement.updateapplies updates to this render object to match a new configuration (i.e., widget).
RenderObjectWidget.createRenderObject) when its element is first mounted. The render object is retained throughout the life of the element, even when the element is deactivated (and the render object is detached).
RenderObjectElement.attachRenderObject). If the element is later deactivated (due to tree grafting), it will be re-attached when the graft is completed (via
RenderObjectElement.inflateWidget, which includes special logic for handling grafting by global key).
RenderObjectWidget.updateRenderObject) when its element is updated (via
Element.update) or rebuilt (via
RenderObjectElement.detachRenderObject) when the element is deactivated. This is generally managed by the parent (via
Element.deactivateChild) and occurs when children are explicitly removed or reparented due to tree grafting. Deactivating a child calls
Element.detachRenderObjectwhich recursively processes descendants until reaching the nearest render object element boundary.
RenderObjectElementoverrides this method to detach its render object, cutting off the recursive walk.
RenderObjectElementand the elements associated with its children. That is, the element tree typically has many more nodes than the render tree.
RenderObjectElementnodes can interact with their render object's parent (via
RenderObjectElement.removeChildRenderObject). Tokens are interpreted in an implementation-specific manner by the ancestor
RenderObjectElementto distinguish render object children.
MultiChildRenderObjectWidget.children). When the element is first mounted, each child is inflated and stored in an internal list (e.g.,
MultiChildRenderObjectElement._children); this list is later used when updating the element.
RenderObjectElement.forgetChild) so that they are excluding from iteration and updating. The old parent removes the child when the element is added to its new parent (this happens during inflation, since grafting requires that the widget tree be updated, too).
MultiChildRenderObjectElementprovide support for common use cases and correspond to the similarly named widget helpers (
RenderObjectWithChildMixinin the render tree.
ContainerRenderObjectMixinmanages children using a linked list.
ComponentElementcomposes other elements. Rather than managing a render object itself, it produces descendant elements that manage their own render objects through building.
Element.rebuildwhich is invoked by the build owner when an element is marked dirty (via
BuildOwner.scheduleBuildFor). Component elements also rebuild when they're first mounted (via
ComponentElement._firstBuild) and when their widget changes (via
StatefulElement, a rebuild may be scheduled spontaneously via
State.setState. In all cases, lifecycle methods are invoked in response to changes to the element tree (for example,
Element.performRebuild. Component elements override
ComponentElement.buildwhereas render object elements update their render object via
ComponentElement.buildprovides a hook for producing intermediate nodes in the element tree.
StatelessElement.buildinvokes the widget’s build method, whereas
StatefulElement.buildinvokes the state’s build method.
ProxyElementsimply returns its widget's child.
Element.updateChild). If the widget is compatible with the existing element, it'll be updated instead of re-inflated. This allows existing render objects to be mutated instead of being recreated. Depending on the mutation, this might involve any combination of layout, painting, and compositing.
Element.reassemble) marks the element as being dirty; most subclasses do not override this behavior. This causes the element tree to be rebuilt during the next frame. Render object elements update their render objects in response to
Element.performRebuildand therefore also benefit from hot reload.
ProxyWidget) participate in the build process;
RenderObjectWidgetsubclasses, generally associated with
RenderObjectElements, do not; these simply update their render object when building.
ComponentElementinstances only have a single child, typically that returned by their widget’s build method (
ProxyElementreturns the child attached to its widget)..
RootRenderObjectElement) assigns a
BuildOwnerfor the element tree. The
BuildOwneris responsible for tracking dirty elements (
BuildOwner.scheduleBuildFor), establishing build scopes wherein elements can be rebuilt / descendant elements can be marked dirty (
BuildOwner.scheduleBuildFor), and unmounting inactive elements at the end of a frame (
BuildOwner.finalizeTree). It also maintains a reference to the root
FocusManagerand triggers reassembly after a hot reload.
ComponentElementis mounted (e.g., after being inflated), an initial build is performed immediately (via
ComponentElement._firstBuild, which calls
Element.markNeedsBuild. This is invoked any time the UI might need to be updated implicitly (or explicitly, in response to
State.setState). This method adds the element to the dirty list and, via
BuildOwner.onBuildScheduled, schedules a frame via
SchedulerBinding.ensureVisualUpdate. The actual build will take place when the next frame is processed.
ComponentElement.mount. In these cases, the intention is to update the element tree immediately.
InheritedElement, each dependent's
Element.didChangeDependenciesis invoked which, by default, marks that element as being dirty. This causes the descendant to rebuild when any of its dependencies change.
BuildOwner.buildScopewill walk the element tree in depth-first order, only considering those nodes that have been marked dirty. By locking the tree and iterating in depth first order, any nodes that become dirty while rebuilding must necessarily be lower in the tree; this is because building is a unidirectional process -- a child cannot mark its parent as being dirty. Thus, it is not possible for build cycles to be introduced and it is not possible for elements that have been marked clean to become dirty again.
ComponentElement.performRebuilddelegates to the
ComponentElement.buildmethod to produce a new child widget for each dirty element. Next,
Element.updateChildis invoked to efficiently reuse or recreate an element for the child. Crucially, if the child’s widget hasn’t changed, the build is immediately cut off. Note that if the child widget did change and
Element.updateis needed, that child will itself be marked dirty, and the build will continue down the tree.
Elementmaintains a map of all
InheritedElementancestors at its location. Thus, accessing dependencies from the build process is a constant time operation.
Element.deactivateChildbecause a child is removed or moved to another part of the tree,
BuildOwner.finalizeTreewill unmount the element if it isn’t reintegrated by the end of the frame.
InheritedElementprovides an efficient mechanism for publishing heritable state to a subset of the element tree. This mechanism depends on support provided by
Element._dependencies, e.g., elements higher in the tree that fill a dependency) and a mapping of all
InheritedElementinstances between this element and the root (
Element._inheritedWidgets). The dependencies set is mainly tracked for debugging purposes .
Element._updateInheritance. By default, elements copy the mapping from their parents. However,
InheritedElementinstances override this method to insert themselves into the mapping (the mapping is always copied so that different branches of the tree are independent).
Element._updateInheritance) when elements are first mounted (via
Element.mount) or are reactivated (via
Element.activate). The mapping is cleared when elements are deactivated (via
Element.deactivate); the element is removed from each of its dependency's dependent lists (
InheritedElement._dependents). As a result, it's usually not necessary to manually walk an element's ancestors.
Element.inheritFromElementis a simple wrapper). In general, the inherited ancestor should be available in
Element._inheritedWidgets. This process causes the inherited element to add the dependent element to its list of dependencies (via
InheritedElementwasn't found in
Element.didChangeDependencies. By default, this method marks the element as being dirty.