void performLayout()

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();
  }