performLayout method
Do the work of computing the layout for this render object.
Do not call this function directly: call layout instead. This function is called by layout when there is actually work to be done by this render object during layout. The layout constraints provided by your parent are available via the constraints getter.
If sizedByParent is true, then this function should not actually change the dimensions of this render object. Instead, that work should be done by performResize. If sizedByParent is false, then this function should both change the dimensions of this render object and instruct its children to layout.
In implementing this function, you must call layout on each of your children, passing true for parentUsesSize if your layout information is dependent on your child's layout information. Passing true for parentUsesSize ensures that this render object will undergo layout if the child undergoes layout. Otherwise, the child can change its layout information without informing this render object.
Some special RenderObject subclasses (such as the one used by OverlayPortal.overlayChildLayoutBuilder) call applyPaintTransform in their performLayout implementation. To ensure such RenderObjects get the up-to-date paint transform, RenderObject subclasses should typically update the paint transform (as reported by applyPaintTransform) in this method instead of paint.
Implementation
@override
void performLayout() {
childManager.didStartLayout();
childManager.setDidUnderflow(false);
final double itemExtent = this.itemExtent;
final double scrollOffset =
constraints.scrollOffset + constraints.cacheOrigin;
assert(scrollOffset >= 0.0);
final double remainingExtent = constraints.remainingCacheExtent;
assert(remainingExtent >= 0.0);
final double targetEndScrollOffset = scrollOffset + remainingExtent;
final BoxConstraints childConstraints = constraints.asBoxConstraints(
minExtent: itemExtent,
maxExtent: itemExtent,
);
final int firstIndex =
getMinChildIndexForScrollOffset(scrollOffset, itemExtent);
final int? targetLastIndex = targetEndScrollOffset.isFinite
? getMaxChildIndexForScrollOffset(targetEndScrollOffset, itemExtent)
: null;
if (firstChild != null) {
final int leadingGarbage = _calculateLeadingGarbage(firstIndex);
final int trailingGarbage = targetLastIndex != null
? _calculateTrailingGarbage(targetLastIndex)
: 0;
collectGarbage(leadingGarbage, trailingGarbage);
//zmt
callCollectGarbage(
collectGarbage: extendedListDelegate.collectGarbage,
leadingGarbage: leadingGarbage,
trailingGarbage: trailingGarbage,
firstIndex: firstIndex,
targetLastIndex: targetLastIndex,
);
} else {
collectGarbage(0, 0);
}
if (firstChild == null) {
if (!addInitialChild(
index: firstIndex,
layoutOffset: indexToLayoutOffset(itemExtent, firstIndex))) {
// There are either no children, or we are past the end of all our children.
final double max;
if (firstIndex <= 0) {
max = 0.0;
} else {
max = computeMaxScrollOffset(constraints, itemExtent);
}
geometry = SliverGeometry(
scrollExtent: max,
maxPaintExtent: max,
);
childManager.didFinishLayout();
return;
}
}
// zmt
handleCloseToTrailingBegin(closeToTrailing);
RenderBox? trailingChildWithLayout;
for (int index = indexOf(firstChild!) - 1; index >= firstIndex; --index) {
final RenderBox? child = insertAndLayoutLeadingChild(childConstraints);
if (child == null) {
// Items before the previously first child are no longer present.
// Reset the scroll offset to offset all items prior and up to the
// missing item. Let parent re-layout everything.
geometry = SliverGeometry(scrollOffsetCorrection: index * itemExtent);
return;
}
final SliverMultiBoxAdaptorParentData childParentData =
child.parentData as SliverMultiBoxAdaptorParentData;
childParentData.layoutOffset = indexToLayoutOffset(itemExtent, index);
assert(childParentData.index == index);
trailingChildWithLayout ??= child;
}
if (trailingChildWithLayout == null) {
firstChild!.layout(childConstraints);
final SliverMultiBoxAdaptorParentData childParentData =
firstChild!.parentData as SliverMultiBoxAdaptorParentData;
childParentData.layoutOffset =
indexToLayoutOffset(itemExtent, firstIndex);
trailingChildWithLayout = firstChild;
}
double estimatedMaxScrollOffset = double.infinity;
for (int index = indexOf(trailingChildWithLayout!) + 1;
targetLastIndex == null || index <= targetLastIndex;
++index) {
RenderBox? child = childAfter(trailingChildWithLayout!);
if (child == null || indexOf(child) != index) {
child = insertAndLayoutChild(childConstraints,
after: trailingChildWithLayout);
if (child == null) {
// We have run out of children.
estimatedMaxScrollOffset = index * itemExtent;
break;
}
} else {
child.layout(childConstraints);
}
trailingChildWithLayout = child;
final SliverMultiBoxAdaptorParentData childParentData =
child.parentData as SliverMultiBoxAdaptorParentData;
assert(childParentData.index == index);
childParentData.layoutOffset =
indexToLayoutOffset(itemExtent, childParentData.index!);
}
final int lastIndex = indexOf(lastChild!);
final double leadingScrollOffset =
indexToLayoutOffset(itemExtent, firstIndex);
double trailingScrollOffset =
indexToLayoutOffset(itemExtent, lastIndex + 1);
///zmt
final double result =
handleCloseToTrailingEnd(closeToTrailing, trailingScrollOffset);
if (result != trailingScrollOffset) {
trailingScrollOffset = result;
estimatedMaxScrollOffset = result;
}
///zmt
final bool lastChildIsFoot = (extendedListDelegate
.lastChildLayoutTypeBuilder
?.call(indexOf(lastChild!)) ??
LastChildLayoutType.none) ==
LastChildLayoutType.foot;
if (lastChildIsFoot) {
//layout as normal constraints
lastChild!.layout(constraints.asBoxConstraints(), parentUsesSize: true);
final double paintExtend = paintExtentOf(lastChild!);
trailingScrollOffset = childScrollOffset(lastChild!)! + paintExtend;
if (trailingScrollOffset < constraints.remainingPaintExtent) {
final SliverMultiBoxAdaptorParentData childParentData =
lastChild!.parentData as SliverMultiBoxAdaptorParentData;
childParentData.layoutOffset =
constraints.remainingPaintExtent - paintExtend;
trailingScrollOffset = constraints.remainingPaintExtent;
}
estimatedMaxScrollOffset = trailingScrollOffset;
}
assert(firstIndex == 0 ||
childScrollOffset(firstChild!)! - scrollOffset <=
precisionErrorTolerance);
assert(debugAssertChildListIsNonEmptyAndContiguous());
assert(indexOf(firstChild!) == firstIndex);
assert(targetLastIndex == null || lastIndex <= targetLastIndex);
estimatedMaxScrollOffset = math.min(
estimatedMaxScrollOffset,
estimateMaxScrollOffset(
constraints,
firstIndex: firstIndex,
lastIndex: lastIndex,
leadingScrollOffset: leadingScrollOffset,
trailingScrollOffset: trailingScrollOffset,
),
);
double paintExtent = calculatePaintOffset(
constraints,
from: leadingScrollOffset,
to: trailingScrollOffset,
);
final double cacheExtent = calculateCacheOffset(
constraints,
from: leadingScrollOffset,
to: trailingScrollOffset,
);
final double targetEndScrollOffsetForPaint =
constraints.scrollOffset + constraints.remainingPaintExtent;
final int? targetLastIndexForPaint = targetEndScrollOffsetForPaint.isFinite
? getMaxChildIndexForScrollOffset(
targetEndScrollOffsetForPaint, itemExtent)
: null;
///zmt
callViewportBuilder(
viewportBuilder: extendedListDelegate.viewportBuilder,
getPaintExtend: (RenderBox? child) {
final LastChildLayoutType lastChildLayoutType = extendedListDelegate
.lastChildLayoutTypeBuilder
?.call(indexOf(child!)) ??
LastChildLayoutType.none;
if (lastChildLayoutType != LastChildLayoutType.none) {
return paintExtentOf(child!);
}
return itemExtent;
});
// fix hittest
if (closeToTrailing) {
paintExtent += closeToTrailingDistance;
}
geometry = SliverGeometry(
scrollExtent: estimatedMaxScrollOffset,
paintExtent: paintExtent,
cacheExtent: cacheExtent,
maxPaintExtent: estimatedMaxScrollOffset,
// Conservative to avoid flickering away the clip during scroll.
hasVisualOverflow: (targetLastIndexForPaint != null &&
lastIndex >= targetLastIndexForPaint) ||
constraints.scrollOffset > 0.0,
);
// We may have started the layout while scrolled to the end, which would not
// expose a new child.
if (estimatedMaxScrollOffset == trailingScrollOffset) {
childManager.setDidUnderflow(true);
}
childManager.didFinishLayout();
}