performLayout method

  1. @override
void performLayout()
override

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.

Implementation

@override
void performLayout() {
  // Ignore the return value of applyViewportDimension because we are
  // doing a layout regardless.
  switch (axis) {
    case Axis.vertical:
      offset.applyViewportDimension(size.height);
    case Axis.horizontal:
      offset.applyViewportDimension(size.width);
  }

  if (center == null) {
    assert(firstChild == null);
    _minScrollExtent = 0.0;
    _maxScrollExtent = 0.0;
    _hasVisualOverflow = false;
    offset.applyContentDimensions(0.0, 0.0);
    return;
  }
  assert(center!.parent == this);

  final (double mainAxisExtent, double crossAxisExtent) = switch (axis) {
    Axis.vertical   => (size.height, size.width),
    Axis.horizontal => (size.width, size.height),
  };

  final double centerOffsetAdjustment = center!.centerOffsetAdjustment;
  final int maxLayoutCycles = _maxLayoutCyclesPerChild * childCount;

  double correction;
  int count = 0;
  do {
    correction = _attemptLayout(mainAxisExtent, crossAxisExtent, offset.pixels + centerOffsetAdjustment);
    if (correction != 0.0) {
      offset.correctBy(correction);
    } else {
      if (offset.applyContentDimensions(
            math.min(0.0, _minScrollExtent + mainAxisExtent * anchor),
            math.max(0.0, _maxScrollExtent - mainAxisExtent * (1.0 - anchor)),
         )) {
        break;
      }
    }
    count += 1;
  } while (count < maxLayoutCycles);
  assert(() {
    if (count >= maxLayoutCycles) {
      assert(count != 1);
      throw FlutterError(
        'A RenderViewport exceeded its maximum number of layout cycles.\n'
        'RenderViewport render objects, during layout, can retry if either their '
        'slivers or their ViewportOffset decide that the offset should be corrected '
        'to take into account information collected during that layout.\n'
        'In the case of this RenderViewport object, however, this happened $count '
        'times and still there was no consensus on the scroll offset. This usually '
        'indicates a bug. Specifically, it means that one of the following three '
        'problems is being experienced by the RenderViewport object:\n'
        ' * One of the RenderSliver children or the ViewportOffset have a bug such'
        ' that they always think that they need to correct the offset regardless.\n'
        ' * Some combination of the RenderSliver children and the ViewportOffset'
        ' have a bad interaction such that one applies a correction then another'
        ' applies a reverse correction, leading to an infinite loop of corrections.\n'
        ' * There is a pathological case that would eventually resolve, but it is'
        ' so complicated that it cannot be resolved in any reasonable number of'
        ' layout passes.',
      );
    }
    return true;
  }());
}