layoutChildSequence method

  1. @override
void layoutChildSequence()
override

Primary work horse of performLayout.

Subclasses must implement this method to layout the children of the viewport. The TwoDimensionalViewportParentData.layoutOffset must be set during this method in order for the children to be positioned during paint. Further, children of the viewport must be laid out with the expectation that the parent (this viewport) will use their size.

child.layout(constraints, parentUsesSize: true);

The primary methods used for creating and obtaining children is buildOrObtainChildFor, which takes a ChildVicinity that is used by the TwoDimensionalChildDelegate. If a child is not provided by the delegate for the provided vicinity, the method will return null, otherwise, it will return the RenderBox of the child.

After layoutChildSequence is completed, any remaining children that were not obtained will be disposed.

Implementation

@override
void layoutChildSequence() {
  _updateAnimationCache();
  if (needsDelegateRebuild || didResize) {
    // Recomputes the tree row metrics, invalidates any cached information.
    _furthestHorizontalExtent = 0.0;
    _updateRowMetrics();
  } else {
    // Updates the visible rows based on cached _rowMetrics.
    _updateFirstAndLastVisibleRow();
  }

  final Alignment resolvedAlignment = alignment.resolve(textDirection);
  _vAlignmentOffset = 0.0;
  if (_rowMetrics.isNotEmpty) {
    final double totalHeight = _rowMetrics[_rowMetrics.length - 1]!.trailingOffset;
    if (totalHeight < viewportDimension.height) {
      _vAlignmentOffset =
          (viewportDimension.height - totalHeight) * (resolvedAlignment.y + 1.0) / 2.0;
    }
  }

  // Ensure vertical scroll bounds are updated before layout. This allows
  // any scroll corrections (e.g., clamping when the tree shrinks) to
  // be applied immediately, ensuring the layout loop builds the rows
  // that will actually be visible at the corrected offset.
  _updateVerticalScrollBounds();

  if (_firstRow == null) {
    // If no rows are visible, we must still update horizontal bounds
    // before returning to ensure the horizontal scroll controller
    // has the latest information.
    _updateHorizontalScrollBounds();
    // To satisfy older framework versions that require at least one vicinity
    // to be laid out (even if no child is built).
    // See also: https://github.com/flutter/flutter/pull/180563
    buildOrObtainChildFor(const TreeVicinity(depth: 0, row: 0));
    // Return early to avoid a framework crash in RenderTwoDimensionalViewport
    // where it expects at least one child to be laid out if the layout
    // pass completes.
    return;
  }

  assert(_lastRow != null);
  _Span rowSpan;
  double rowOffset =
      -verticalOffset.pixels + _rowMetrics[_firstRow!]!.leadingOffset + _vAlignmentOffset;
  for (int row = _firstRow!; row <= _lastRow!; row++) {
    rowSpan = _rowMetrics[row]!;
    final double rowHeight = rowSpan.extent;
    if (_animationLeadingIndices.keys.contains(row)) {
      rowOffset -= rowSpan.animationOffset;
    }
    rowOffset += rowSpan.configuration.padding.leading;

    final vicinity = TreeVicinity(depth: _rowDepths[row]!, row: row);
    final RenderBox child = buildOrObtainChildFor(vicinity)!;
    final TwoDimensionalViewportParentData parentData = parentDataOf(child);
    final childConstraints = BoxConstraints(
      minHeight: rowHeight,
      maxHeight: rowHeight,
      // Width is allowed to be unbounded.
    );
    child.layout(childConstraints, parentUsesSize: true);
    parentData.layoutOffset = Offset(
      (_rowDepths[row]! * indentation) - horizontalOffset.pixels,
      rowOffset,
    );
    rowOffset += rowHeight + rowSpan.configuration.padding.trailing;
    _furthestHorizontalExtent = math.max(
      parentData.layoutOffset!.dx + horizontalOffset.pixels + child.size.width,
      _furthestHorizontalExtent,
    );
  }
  _updateHorizontalScrollBounds();
}