SliverLogicalParentDatato support multiple box children (via
RenderBox>) as well as the keep alive protocol (via
KeepAliveParentDataMixin), allowing certain children to be cached when not visible. In addition to tracking each child’s layout offset and neighbors in the child list, this parent data also contains an integer index assigned by the manager.
SliverGridParentDatais a subclass of
SliverMultiBoxAdaptorParentDatathat also tracks the child’s cross axis offset (i.e., the distance from the child’s left or top edge from the parent’s left or top edge, depending on whether the axis is vertical or horizontal, respectively).
RenderSliverMultiBoxAdaptorprovides a base class for slivers that manage multiple box children to efficiently fill remaining space in the viewport. Generally, subclasses will create children dynamically based on the current scroll offset and remaining paint extent. A manager (the
RenderSliverBoxChildManager) provides an interface to create, track, and remove children in response to layout; this allows a clean separation between how children are laid out and how they are produced. Subclasses implement layout themselves, delegating to the superclass as needed. Only visible and cached children are associated with the render object at any given time; cached children are managed using the keep alive protocol (via
RenderSliverBoxChildManagerprovides a bidirectional interface for managing, creating, and removing box children. This interface serves to decouple the
RenderSliverMultiBoxAdaptorfrom the source of children.
RenderSliverBoxChildManagerinterface to link element creation and destruction to an associated widget’s
SliverChildDelegate. This delegate typically provides (or builds) children on demand.
RenderSliverFixedExtentBoxAdaptoris a subclass of
RenderSliverMultiBoxAdaptorthat arranges a sequence of box children with equal main axis extent. This is more efficient since the adaptor may reckon each child’s position directly (i.e., without layout). Children that are laid out are provided tight constraints.
SliverChildDelegateis a subclass that assists in generating children dynamically and destroying those children when they’re no longer needed (it also supports destruction mitigation to avoid rebuilding expensive subtrees). Children are provided on demand (i.e., lazily) using a build method that accepts an index and produces the corresponding child.
SliverChildListDelegateis a subclass of
SliverChildDelegatethat provides children using an explicit list. This negates the benefit of only building children when they are in view. This delegate may also wrap produced children in
SliverChildBuilderDelegateis a subclass of
SliverChildDelegatethat provides children by building them on demand. This improves performance by only building those children that are currently visible. This delegate may also wrap produced children in
RenderSliverWithKeepAliveMixinto ensure that its associated parent data incldues the
KeepAliveParentDataMixin. This mixin introduces “
keepAlive” / “
keptAlive” flags that form the basis of all caching decisions (the former indicates whether keep alive behavior is requested; the latter indicates whether the item is currently being kept alive).
keepAlive” flag signals that the associated child is to be retained even when it would have been destroyed. This flag is altered by
KeepAlive, a parent data widget associated with the
KeepAlivewidget can be applied out of turn (i.e.,
KeepAlive.debugCanApplyOutOfTurnreturns true). This implies that the associated element can alter the child’s parent data without triggering a rebuild (via
ParentDataElement.applyWidgetOutOfTurn). This allows parent data to be altered in the middle of building, layout, and painting.
AutomaticKeepAlive). Since enabling this feature will never invalidate the parent’s own layout (see discussion above), this is always safe. The benefit is that the bit can be flipped without requesting another frame.
AutomaticKeepAlivemanages an immediate
KeepAlivechild based on the
KeepAliveNotificationsemitted by descendent widgets.
AutomaticKeepAliveClientMixinprovides helpers to help
Statesubclasses appropriately manage
keepAlive” flag is honored by
RenderSliverMultiBoxAdaptor, which maintains a keep alive bucket and ensures that the associated children remain in the render tree but are not actually rendered.
RenderSliverBoxChildManager.childCount: the manager must provide an accurate child count if there are finite children.
RenderSliverBoxChildManager.didAdoptChild: invoked when a child is adopted (i.e., added to the child list) or needs a new index (e.g., moved within the list). This is the only place where the child’s index is set.
RenderSliverMultiBoxAdaptor.move, which is called indirectly by
RenderObjectElement.updateChildwhen the child order changes) will always call
RenderSliverBoxChildManager.didAdoptChildto update the index.
RenderSliverBoxChildManager.didFinishLayout: invoked at the beginning and end of layout.
SliverMultiBoxAdaptorElement, the latter invokes a corresponding method on the delegate providing the first and last indices that are visible in the list.
RenderSliverBoxChildManager.setDidUnderflow: indicates that the manager could not fill all remaining space with children. Generally invoked unconditionally at the beginning of layout (i.e., without underflow), then again if there is space left over in the viewport (i.e., with underflow).
RenderSliverBoxChildManager.estimateMaxScrollOffset: estimates the maximum scroll extent that can be consumed by all visible children. Provided information about the children current in view (first and last indices and leading and trailing scroll offsets).
SliverMultiBoxAdaptorElement, this defers to the associated widget. If no implementation is provided, it multiples the average extent (calculated for all visible children) by the overall child count to obtain an approximate answer.
RenderSliverBoxChildManager.createChild: returns a child with the given index after incorporating it into the render object (creates the initial child if a position isn’t specified). May cache previously built children.
SliverMultiBoxAdaptorElement, this method utilizes
Element.updateChildto update or inflate a new child using the widget tree built by a
Element.inflateWidgetcauses the render object (
RenderSliverMultiBoxAdaptor) to create and mount the child, which inserts it into a slot associated with the child’s index (via
SliverMultiBoxAdaptorElement.insertChildRenderObject). This invokes
RenderSliverMultiBoxAdaptor.insertto update the render object’s child list and adopt the new child. This calls back into the manager, which sets the index appropriately (the index is stored in a member variable).
RenderSliverBoxChildManager.removeChild: removes a child, generally in response to “garbage collection” when the child is no longer visible; also used to destroy a child that was previously kept alive.
SliverMultiBoxAdaptorElement, this method invokes
Element.updateChildwith the new widget set to null, causing the child to be deactivated and detached. This invokes
SliverMultiBoxAdaptorElement.removeChildRenderObjectusing the child’s index as the slot identifier, which calls
RenderSliverMultiBoxAdaptor.removeto update the render object’s child list. Once updated, the child render object is dropped and removed. If the child had been kept alive, it is removed from the keep alive bucket, instead.
SliverChildDelegatein particular. As the list’s manager (and a convenient integration point between the render tree and the widget),
SliverMultiBoxAdaptorElementfacilitates the process.
SliverMultiBoxAdaptorElementtracks when children are created and destroyed (either explicitly or during a rebuild), setting
SliverMultiBoxAdaptorElement._currentlyUpdatingChildIndexto the intended index just before the render tree is updated. The index also serves as the slot for overridden
Flutter’s usual flow), which invokes
RenderSliverBoxChildManager.didAdoptChildany time the index might change.
ContainerRenderObjectMixin, and a keep alive bucket, managed separately.
ContainerRenderObjectMixindo not interact with these children (though a few operations have been overridden).
RenderSliverMultiBoxAdaptor._createOrObtainChild, which consults the keep alive bucket before requesting the manager to provide a child. Note that the manager incorporates an additional caching layer (i.e., to avoid rebuilding children unless the entire list has been rebuilt).
RenderSliverMultiBoxAdaptor._destroyOrCacheChild, which consults the keep alive flag before requesting the manager to remove the child.
RenderSliverMultiBoxAdaptor.insertcannot be used to add children to the keep alive bucket.
RenderSliverMultiBoxAdaptor.addInitialChildto allow the initial layout offset and starting index to be specified. The child is sourced from the keep alive bucket or, if not found, the manager. If no child can be provided, underflow is indicated. Importantly, this child is not laid out.
RenderSliverMultiBoxAdaptor.insertAndLayoutChild. The former method positions children at the head of the child list whereas the latter positions them at its tail.
RenderSliverMultiBoxAdaptor.firstChild; non-leading children may or may not become
RenderSliverMultiBoxAdaptor.lastChilddepending on where they’re inserted.
RenderSliverMultiBoxAdaptor.collectGarbage. This method destroys a number of children at the start and end of the list. It also cleans up keep alive overhead, and so must always be called during layout.
Element.updateChild) will modify fields in that render object. Most render objects will schedule layout, painting, etc., in response to such changes.
SliverMultiBoxAdaptorElement) maintains an ordered cache of all child elements and widgets that have been built since the list itself was rebuilt. This list includes keep alive entries.
SliverChildDelegate.findIndexByKey), that child is unconditionally assigned its new index.
SliverMultiBoxAdaptorElement.updateChild). This will attempt to reuse elements where possible. If a candidate cannot be built (i.e., because the delegate does not build anything at that index), it is deactivated and removed.
SliverMultiBoxAdaptorElement._currentBeforeChild. This serves as the “after” argument whenever the child list is manipulated, preserving the index order.
RenderSliverFixedExtentListis a trivial
RenderSliverFixedExtentBoxAdaptorsubclass that lays out a sequence of children using a fixed main axis extent in order and without gaps (cross axis extent is determined from the incoming constraints).
RenderSliverFixedExtentBoxAdaptor.getMaxChildIndexForScrollOffset: returns the maximum index for the child at the current scroll offset or earlier.
RenderSliverFixedExtentBoxAdaptor.getMinChildIndexForScrollOffset: returns the minimum index for the child at the current scroll offset or later.
SliverChildDelegatein use, this might lead to the interleaving of build and layout phases (i.e., if children are built on demand rather than provided up front). Indices are assigned sequentially as layout progresses and relevant lifecycle methods within the manager are invoked (e.g.,
RenderSliverMultiBoxAdaptor.collectGarbage). This also cleans up any expired keep alive children.
RenderSliverMultiBoxAdaptor.addInitialChild). This establishes the initial index and layout offset as all other children are positioned relative to this one.
SliverGeometry.scrollExtent: estimated maximum extent (this is correct for fixed extent lists).
SliverGeometry.maxPaintExtent: estimated maximum extent (this is the most that can be painted).
SliverGeometry.paintExtent: the visible portion of the range defined by the leading and trailing scroll offsets.
SliverGeometry.hasVisualOverflow: true if the target index wasn’t reached or the list precedes the viewport’s leading edge (i.e., incoming scroll offset is greater than zero).