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() {
  assert(_debugHasNecessaryDirections);
  final BoxConstraints constraints = this.constraints;

  final _sizes = _computeSizes(
    constraints: constraints,
    layoutChild: (child, constraints) {
      child.layout(constraints, parentUsesSize: true);
      final childParentData = child.parentData as BoxyFlexParentData;
      childParentData._tempSize = child.size;
      return child.size;
    },
  );

  var crossSize = _sizes.crossSize;
  var maxBaselineDistance = 0.0;
  if (crossAxisAlignment == CrossAxisAlignment.baseline) {
    var child = firstChild;
    double maxSizeAboveBaseline = 0;
    double maxSizeBelowBaseline = 0;
    while (child != null) {
      assert(() {
        if (textBaseline == null)
          throw FlutterError('To use FlexAlignItems.baseline, you must also specify which baseline to use using the "baseline" argument.');
        return true;
      }());
      final double? distance = child.getDistanceToBaseline(textBaseline!, onlyReal: true);
      if (distance != null) {
        maxBaselineDistance = math.max(maxBaselineDistance, distance);
        maxSizeAboveBaseline = math.max(
          distance,
          maxSizeAboveBaseline,
        );
        maxSizeBelowBaseline = math.max(
          child.size.height - distance,
          maxSizeBelowBaseline,
        );
        crossSize = math.max(maxSizeAboveBaseline + maxSizeBelowBaseline, crossSize);
      }
      final FlexParentData childParentData = child.parentData as FlexParentData;
      child = childParentData.nextSibling;
    }
  }

  // Align items along the main axis.
  final mainSize = _sizes.mainSize;
  size = SizeAxisUtil.create(_direction, crossSize, mainSize);
  final actualSize = size.axisSize(_direction);
  final actualSizeDelta = actualSize - _sizes.allocatedSize;
  _overflow = math.max(0.0, -actualSizeDelta);
  final remainingSpace = math.max(0.0, actualSizeDelta);
  late double leadingSpace;
  late double betweenSpace;

  // flipMainAxis is used to decide whether to lay out left-to-right/top-to-bottom (false), or
  // right-to-left/bottom-to-top (true). The _startIsTopLeft will return null if there's only
  // one child and the relevant direction is null, in which case we arbitrarily decide not to
  // flip, but that doesn't have any detectable effect.
  final bool flipMainAxis = !(_startIsTopLeft(direction, textDirection, verticalDirection) ?? true);
  switch (_mainAxisAlignment) {
    case MainAxisAlignment.start:
      leadingSpace = 0.0;
      betweenSpace = 0.0;
      break;
    case MainAxisAlignment.end:
      leadingSpace = remainingSpace;
      betweenSpace = 0.0;
      break;
    case MainAxisAlignment.center:
      leadingSpace = remainingSpace / 2.0;
      betweenSpace = 0.0;
      break;
    case MainAxisAlignment.spaceBetween:
      leadingSpace = 0.0;
      betweenSpace = childCount > 1 ? remainingSpace / (childCount - 1) : 0.0;
      break;
    case MainAxisAlignment.spaceAround:
      betweenSpace = childCount > 0 ? remainingSpace / childCount : 0.0;
      leadingSpace = betweenSpace / 2.0;
      break;
    case MainAxisAlignment.spaceEvenly:
      betweenSpace = childCount > 0 ? remainingSpace / (childCount + 1) : 0.0;
      leadingSpace = betweenSpace;
      break;
  }

  // Position elements
  double childMainPosition = flipMainAxis ? actualSize - leadingSpace : leadingSpace;
  var child = firstChild;
  while (child != null) {
    final FlexParentData childParentData = child.parentData as FlexParentData;
    final double childCrossPosition;
    switch (_crossAxisAlignment) {
      case CrossAxisAlignment.start:
      case CrossAxisAlignment.end:
        childCrossPosition = _startIsTopLeft(flipAxis(direction), textDirection!, verticalDirection)
            == (_crossAxisAlignment == CrossAxisAlignment.start)
            ? 0.0
            : crossSize - child.size.crossAxisSize(_direction);
        break;
      case CrossAxisAlignment.center:
        childCrossPosition = crossSize / 2.0 - child.size.crossAxisSize(_direction) / 2.0;
        break;
      case CrossAxisAlignment.stretch:
        childCrossPosition = 0.0;
        break;
      case CrossAxisAlignment.baseline:
        if (_direction == Axis.horizontal) {
          assert(textBaseline != null);
          final double? distance = child.getDistanceToBaseline(textBaseline!, onlyReal: true);
          if (distance != null)
            childCrossPosition = maxBaselineDistance - distance;
          else
            childCrossPosition = 0.0;
        } else {
          childCrossPosition = 0.0;
        }
        break;
    }
    if (flipMainAxis)
      childMainPosition -= child.size.axisSize(_direction);

    childParentData.offset = OffsetAxisUtil.create(
      _direction,
      childCrossPosition,
      childMainPosition,
    );

    if (flipMainAxis) {
      childMainPosition -= betweenSpace;
    } else {
      childMainPosition += child.size.axisSize(_direction) + betweenSpace;
    }
    child = childParentData.nextSibling;
  }
}