RenderObjectprovides the basic infrastructure for managing a tree of visual elements. Render objects define a general protocol for performing layout, painting, and compositing. This protocol is largely abstract, deferring to subclasses to determine the inputs and outputs of layout, how hit testing is performed (though all render objects are
HitTestTargets), and how the render object hierarchy is modeled (
AbstractNode, which doesn't specify a concrete child model).
Constraintsrepresent the immutable inputs to layout. The definition is flexible provided that constraints can indicate whether they represent a single configuration (
Constraints.isTight), are expressed in the canonical form (
Constraints.isNormalized), and can be compared for equality (via
ParentDatarepresents opaque data stored in a child by its parent. This data is typically considered an implementation detail of the parent and should therefore not be accessed by the child. Parent data is flexibly defined and only includes a single method (
ParentData.detach, which allows instances to react to their render object being removed from the tree).
PipelineOwneras part of
RendererBinding.initInstances(i.e., the binding’s “constructor”). The pipeline owner serves as the render tree's
AbstractNode.attachand propagated by
PipelineOwneris analogous to the
BuildOwner, tracking which render objects need compositing bits, layout, painting, or semantics updates. Objects mark themselves as being dirty by adding themselves to specific lists (e.g.,
PipeplineOwner._nodesNeedingPaint). All dirty objects are later cleaned by a corresponding “flush” method (e.g,
PipelineOwner.flushPaint). These methods initiate the corresponding rendering process and are invoked every frame as needed (via
PipelineOwner.flushLayoutlays out all dirty render objects (laying out dirty children recursively, based on each
PipelineOwner.flushCompositingBitsupdates a cache used to indicate whether descendant render objects create new layers. If so, because painting is often interlaced (i.e., a render object paints before and after its children), certain operations may need to be implemented differently. If all descendants paint into the same layer, an operation (like clipping) can be performed using a single painting context. If not, some effects can only be achieved by inserting new layers. These bits determine which option will be used when painting.
PipelineOwner.flushPaintpaints all dirty render objects (painting dirty children recursively, based on each
PipelineOwner.flushSemanticscompiles semantic data for all dirty render objects.
PipelineOwner.requestVisualUpdateto schedule a frame and eventually update the user interface. Requesting a visual update invokes the visual update handler that was bound when the
PipelineOwnerwas constructed by
RendererBinding.ensureVisualUpdatewhich schedules a frame (via
SchedulerBinding.scheduleFrame) if it's not possible to apply the visual update during the current frame (e.g., during post frame callbacks).
RenderObject.attach), it adds itself to all relevant dirty lists (e.g.,
RenderObject._needsPaint). Several other bits depend on the configuration of the node (e.g.,
RenderObject._needsCompositing) but will be true if they're applicable.
RenderObject.scheduleInitialSemantics). This flow occurs when the
RenderViewis first constructed (via
RendererBinding.initRenderView). This process calls
RenderView.prepareInitialFrame, which schedules initial layout and painting for the
RenderView(semantics follows a slightly different flow). Marking the root node as being dirty causes the entire tree to perform layout, painting, and semantics as needed.
RendererBinding.performReassemble) which cascades down the render tree. By default, render objects mark themselves as being dirty for layout, compositing bits, painting, and semantics (via
RenderObjectinstances, a subclass of
RenderObject.dropChildmust be called whenever a child is added or removed, respectively. Modifying the child model marks the subtree dirty for layout, painting, semantics, and compositing bits.
RenderObject.redepthChildmust be called whenever a child's depth changes.
RenderObject.attach) and detached when they should no longer be rendered (via
PipelineOwnermaintains a reference to a root node (
PipelineOwner.rootNode). This field is set to the application's
RenderViewwhen initializing the
RendererBinding.renderViewand, via a setter,
PipelineOwner.rootNode). This automatically detaches the old render object (if any) and attaches the new one.
ContainerRenderObjectMixin) must override
RenderObject.redepthChildon each child.
RenderObject.parent) and an attached state (
RenderObject.attached), indicating whether they'll contribute to rendering.
ParentDatasubclass; this data may only be assigned using
RenderObject.setupParentData. By convention, this data is considered opaque to the child, though a protocol is free to alter this rule (but shouldn’t). The parent data may then be mutated (e.g., via
ContainerRenderObjectMixinuses this field to implement a linked list of children.
RenderObjectsimplement the visitor pattern via
RenderObject.RenderObject.visitChildrenwhich invokes a
RenderObjectVisitoronce per child.
RenderObject.detachmethods must call their counterpart on each child.
RenderObject.visitChildrenmethods must recursively call
RenderObject.redepthChildand the visitor argument on each child, respectively.
RenderObjectWithChildMixinstores a single child reference within a render object.
RenderObjectWithChildMixin.child) adopts and drops the child as appropriate (i.e., when adding and removing a child).
ContainerRenderObjectMixinuses each child’s parent data (which must implement
ContainerParentDataMixin) to maintain a doubly linked list of children (via
ContainerParentDataMixin._removeFromChildList). Insertions can be positional using an optional preceding child argument.
ContainerRenderObjectMixin.remove). These adopt and drop children as needed. A move operation is provided to avoid unnecessarily dropping and re-adopting a child.
RenderObject.markNeedsLayoutis invoked (as this may alter layout).
ContainerRenderObjectMixin.firstChild), last child (
ContainerRenderObjectMixin.lastChild), and total number of children (
ContainerRenderObjectMixin.childCount) is accessible via the render object.
RenderObject.markNeedsLayout); this schedules a layout pass during the next frame.
PipelineOwner.flushLayoutwhen the corresponding dirty list is non-empty (
RenderObject.layoutis invoked with a
Constraintssubclass as input; some subclasses incorporate other out-of-band input, too. The layout method applies input to produce geometry (generally a size, though the type is unconstrained).
parentUsesSizeargument to layout.
RenderObjectsthat solely determine their sizing using the input constraints set
RenderObject.sizedByParentto true and perform all layout in
RenderObject.paint) is divided into layers (
Layer) before being rasterized and uploaded to the engine. Some render objects share the current layer whereas others introduce one or more new layers.
RenderObject.layer). This allows the subtree rooted at this node to paint separately from its parent. Moreover, the offset layer can be re-used (and translated) to avoid unnecessary repainting.
RenderObject.layerto allow future painting operations to update the existing layer rather than creating a new one.
Layerpreserves a reference to any underlying
oldLayerargument when building a new scene.
needsCompositingindicates whether the subtree rooted at a render object (e.g., the object and its descendants) may introduce new layers. This indicates to the
PaintingContextthat certain operations (e.g., clipping) will need to be implemented differently.
RenderObject.markNeedsCompositingBitsUpdate) whenever the render tree is mutated; children that are added may introduce layers whereas children that are removed may allow a single layer to be shared.
RenderObject.dropChildalways call this method.
PipelineOwner.flushCompositingBitswhen the corresponding dirty list is non-empty (
PipelineOwner._nodesNeedingCompositingBitesUpdate). The update must be applied before painting.
RenderObject.depth) then updated (via
RenderObject._updateCompositingBits). This method performs a depth-first traversal of the render tree, marking each render object as needing compositing if any of its children need compositing (or it's a repaint boundary or always needs compositing).
RenderObject.markNeedsPaint); this schedules a painting pass during the next frame.
PipelineOwner.flushPaintwhen the corresponding dirty list is non-empty (
RenderObject.depth) then painted (via
PaintingContext.repaintCompositedChild). This requires the render object to have an associated layer (only render objects that are repaint boundaries are added to this list).
RenderObject._skippedPaintingOnLayer). Each is marked dirty to ensure that it's eventually painted.
PaintingContextis created for the render object and forwarded to
RenderObject.paintwith the provided context.
RenderObject.paintBoundsprovides an estimate of how large a region this render object will paint (as a
Rectin local coordinates). This doesn't need to match the render object's layout geometry.
RenderObject.applyPaintTransformcaptures how a render object transforms its children by applying the same transformation to a provided matrix (e.x., if the child is painted at
(x, y), the matrix is translated by
(x, y)). This allows the framework to assemble a sequence of transformations to map between any two render object's coordinate systems (via
RenderObject.getTransformTo). This process allows local coordinates to be mapped to global coordinates and vice versa.
HitTestTargetand can therefore process incoming pointer events (via
RenderObject.handleEvent). However, render objects do not support hit testing: there is no builtin mechanism for a render object to subscribe to a pointer. This functionality is introduced by
GestureBindingreceives pointer events from the engine (via
Window.onPointerDataPacket). These are queued (via
GestureBinding._handlePointerDataPacket), which is flushed immediate (via
GestureBinding._flushPointerEventQueue) or when events are unlocked (via
GestureBinding.unlocked, which is called by the
GestureBinding._handlePointerEventprocesses each queued event in sequence using a table of hit test results (
GestureBinding._hitTests) and a pointer router (
PointerRouterinstance that maps an integral pointer ID to an event handler callback (
PointerRoute, a function that accepts a
PointerEvent). The router also captures a transform matrix to map from screen coordinates to local coordinates.
HitTestResult). Each entry (
HitTestEntry) records the recipient object (
HitTestTarget) and the transform needed to map from global coordinates to local coordinates (
HitTestTargetis an object that can handle pointer events (via
HitTestTarget.handleEvent). Render objects are hit test targets.
PointerSignalEvent) trigger a hit test (via
GestureBinding.hitTest). Ongoing events (
PointerMoveEvent) are forwarded to the path associated with the initiating event. Terminating events (
PointerCancelEvent) remove the entry.
GestureBindingdefines a default override that adds itself to the
HitTestResult; invoking this also invokes any overrides mixed into the bindings. The binding processes events to resolve gesture recognizers using the gesture arena (via
RendererBindingprovides an override that tests the render view first (via
RenderView.hitTest, called by
RendererBinding). This is the entry point for hit testing in the render tree.
RenderBox.hitTest) before adding itself to the result.
HitTestablebut do provide a compatible interface (used by
RenderObjectdoes not participate in hit testing by default.
HitTestableis only used at the binding level. All other hit testing is implementation dependent.
GestureBinding.dispatchEvent) in depth-first order.
HitTestTarget.processEvent). As a
HitTestTarget, render objects are eligible to process pointers. However, there is no mechanism for them to participate in hit testing.
RenderObject.paint. The same transform is applied logically (i.e., when hit testing, computing semantics, mapping coordinates, etc.) via
RenderObject.applyPaintTransform. This method applies the child transformation to the provided matrix (
providedMatrix * childTransform).
RenderObject.transformTochains paint transformation matrices mapping from the current coordinate space to an ancestor’s coordinate space.
RenderView; to obtain physical screen coordinates, these coordinates must be further transformed by
ViewConfigurationfrom the platform that specifies the screen's dimensions (
ViewConfiguration.size) and the pixel depth (
RenderView.performLayoutadopts this size (
RenderView.size) and, if present, lays out the render box child with tight constraints corresponding to the screen's dimensions (
RenderView.hitTestdelegate to the child, if present; the render view always adds itself to the hit test result.
RenderView.configuration), the layer tree's root is replaced with a
RenderView._updateMatricesAndCreateNewRootLayer) that converts physical pixels to logical pixels. This allows all descendant render objects to be isolated from changes to screen resolution.
RenderView.applyPaintTransformapplies the resolution transform (
RenderView._rootTransform) to map from logical global coordinates to physical screen coordinates.
RenderView.compositeFrameimplements the final stage of the rendering pipeline, compositing all painted layers and uploading the resulting scene to the engine for rasterization (via
RenderView._updateSystemChromewill query the layer tree to find
SystemUiOverlayStyleinstances associated with the very top and very bottom of the screen (this is the mechanism used to determine how to tint system UI like the status bar or Android's bottom navigation bar). If found, the appropriate settings are communicated to the platform (using