in packages/devtools_app/lib/src/primitives/extent_delegate_list.dart [334:511]
void performLayout() {
childManager.didStartLayout();
childManager.setDidUnderflow(false);
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 int firstIndex =
_extentDelegate.minChildIndexForScrollOffset(scrollOffset);
final int targetLastIndex = targetEndScrollOffset.isFinite
? _extentDelegate.maxChildIndexForScrollOffset(targetEndScrollOffset)
: null;
if (firstChild != null) {
final int leadingGarbage = _calculateLeadingGarbage(firstIndex);
final int trailingGarbage = _calculateTrailingGarbage(targetLastIndex);
collectGarbage(leadingGarbage, trailingGarbage);
} else {
collectGarbage(0, 0);
}
if (firstChild == null) {
if (!addInitialChild(
index: firstIndex,
layoutOffset: _extentDelegate.layoutOffset(firstIndex),
)) {
// There are either no children, or we are past the end of all our children.
// If it is the latter, we will need to find the first available child.
double max;
if (childManager.childCount != null) {
// TODO(jacobr): is this correct?
// This matches the logic from sliver_fixed_extent_list but it seems
// a little odd. This is only right if the childManager contains all
// children added to the list view.
max = _extentDelegate.layoutOffset(childManager.childCount);
} else if (firstIndex <= 0) {
max = 0.0;
} else {
// We will have to find it manually.
int possibleFirstIndex = firstIndex - 1;
while (possibleFirstIndex > 0 &&
!addInitialChild(
index: possibleFirstIndex,
layoutOffset: _extentDelegate.layoutOffset(possibleFirstIndex),
)) {
possibleFirstIndex -= 1;
}
max = _extentDelegate.layoutOffset(possibleFirstIndex);
}
assert(max >= 0.0);
geometry = SliverGeometry(
scrollExtent: _extentDelegate.layoutOffset(_extentDelegate.length),
maxPaintExtent: max,
);
childManager.didFinishLayout();
return;
}
}
RenderBox trailingChildWithLayout;
for (int index = indexOf(firstChild) - 1; index >= firstIndex; --index) {
final RenderBox child =
insertAndLayoutLeadingChild(buildChildConstraints(index));
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: _extentDelegate.layoutOffset(index),
);
return;
}
final SliverMultiBoxAdaptorParentData childParentData = child.parentData;
childParentData.layoutOffset = _extentDelegate.layoutOffset(index);
assert(childParentData.index == index);
trailingChildWithLayout ??= child;
}
if (trailingChildWithLayout == null) {
firstChild.layout(buildChildConstraints(firstIndex));
final SliverMultiBoxAdaptorParentData childParentData =
firstChild.parentData;
childParentData.layoutOffset = _extentDelegate.layoutOffset(firstIndex);
trailingChildWithLayout = firstChild;
}
double estimatedMaxScrollOffset =
_extentDelegate.layoutOffset(_extentDelegate.length);
for (int index = indexOf(trailingChildWithLayout) + 1;
targetLastIndex == null || index <= targetLastIndex;
++index) {
RenderBox child = childAfter(trailingChildWithLayout);
if (child == null || indexOf(child) != index) {
child = insertAndLayoutChild(
buildChildConstraints(index),
after: trailingChildWithLayout,
);
if (child == null) {
// We have run out of children.
estimatedMaxScrollOffset = _extentDelegate.layoutOffset(index + 1);
break;
}
} else {
child.layout(buildChildConstraints(index));
}
trailingChildWithLayout = child;
assert(child != null);
final SliverMultiBoxAdaptorParentData childParentData = child.parentData;
assert(childParentData.index == index);
childParentData.layoutOffset =
_extentDelegate.layoutOffset(childParentData.index);
}
final int lastIndex = indexOf(lastChild);
final double leadingScrollOffset = _extentDelegate.layoutOffset(firstIndex);
final double trailingScrollOffset =
_extentDelegate.layoutOffset(lastIndex + 1);
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,
),
);
final 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
? _extentDelegate
.maxChildIndexForScrollOffset(targetEndScrollOffsetForPaint)
: null;
assert(paintExtent <= estimatedMaxScrollOffset);
geometry = SliverGeometry(
scrollExtent: _extentDelegate.layoutOffset(_extentDelegate.length),
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();
}