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() {
  super.performLayout();

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

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


  double mainAxisExtent;
  double crossAxisExtent;
  switch (axis) {
    case Axis.vertical:
      mainAxisExtent = size.height;
      crossAxisExtent = size.width;
      break;
    case Axis.horizontal:
      mainAxisExtent = size.width;
      crossAxisExtent = size.height;
      break;
  }

  double centerOffsetAdjustment = center!.centerOffsetAdjustment;

  // 仅在倒序展示的时候进行测算
  if (axisDirection == AxisDirection.up) {
    double totalLayoutExtent = 0;
    RenderSliver? p = firstChild;
    while (p != null) {
      var geometry = p.geometry;
      double extent = geometry?.scrollExtent ?? 0.0;
      totalLayoutExtent += extent;
      p = childAfter(p);
    }
    // 列表总高度小于视图高度的时候,重新布局,给定一定的偏移,让列表依旧展示在顶部
    if (totalLayoutExtent > 0 && mainAxisExtent > totalLayoutExtent) {
      centerOffsetAdjustment -= (mainAxisExtent - totalLayoutExtent);
    } else {
      // 如果列表高度超过视图高度,此时无需再重新布局了
      return;
    }
  }

  double correction;
  int count = 0;
  do {
    assert(offset.pixels != null);
    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;
  }());
}