layout method Null safety

  1. @override
void layout(
  1. Context context,
  2. BoxConstraints constraints,
  3. {bool parentUsesSize = false}
)
override

First widget pass to calculate the children layout and bounding box

Implementation

@override
void layout(Context context, BoxConstraints constraints,
    {bool parentUsesSize = false}) {
  // Determine used flex factor, size inflexible items, calculate free space.
  var totalFlex = 0;
  Widget? lastFlexChild;

  final maxMainSize = direction == Axis.horizontal
      ? constraints.maxWidth
      : constraints.maxHeight;
  final canFlex = maxMainSize < double.infinity;

  var crossSize = 0.0;
  var allocatedSize = 0.0; // Sum of the sizes of the non-flexible children.
  var index = _context.firstChild;

  for (var child in children.sublist(_context.firstChild)) {
    final flex = child is Flexible ? child.flex : 0;
    final fit = child is Flexible ? child.fit : FlexFit.loose;
    if (flex > 0) {
      assert(() {
        final dimension = direction == Axis.horizontal ? 'width' : 'height';
        if (!canFlex &&
            (mainAxisSize == MainAxisSize.max || fit == FlexFit.tight)) {
          throw Exception(
              'Flex children have non-zero flex but incoming $dimension constraints are unbounded.');
        } else {
          return true;
        }
      }());
      totalFlex += flex;
    } else {
      BoxConstraints? innerConstraints;
      if (crossAxisAlignment == CrossAxisAlignment.stretch) {
        switch (direction) {
          case Axis.horizontal:
            innerConstraints = BoxConstraints(
                minHeight: constraints.maxHeight,
                maxHeight: constraints.maxHeight);
            break;
          case Axis.vertical:
            innerConstraints = BoxConstraints(
                minWidth: constraints.maxWidth,
                maxWidth: constraints.maxWidth);
            break;
        }
      } else {
        switch (direction) {
          case Axis.horizontal:
            innerConstraints =
                BoxConstraints(maxHeight: constraints.maxHeight);
            break;
          case Axis.vertical:
            innerConstraints = BoxConstraints(maxWidth: constraints.maxWidth);
            break;
        }
      }
      child.layout(context, innerConstraints, parentUsesSize: true);
      assert(child.box != null);
      allocatedSize += _getMainSize(child);
      crossSize = math.max(crossSize, _getCrossSize(child));
      if (direction == Axis.vertical &&
          allocatedSize > constraints.maxHeight) {
        break;
      }
    }
    lastFlexChild = child;
    index++;
  }
  _context.lastChild = index;
  final totalChildren = _context.lastChild - _context.firstChild;

  // Distribute free space to flexible children, and determine baseline.
  final freeSpace =
      math.max(0.0, (canFlex ? maxMainSize : 0.0) - allocatedSize);
  var allocatedFlexSpace = 0.0;
  if (totalFlex > 0) {
    final spacePerFlex =
        canFlex && totalFlex > 0 ? (freeSpace / totalFlex) : double.nan;

    for (var child in children) {
      final flex = child is Flexible ? child.flex : 0;
      final fit = child is Flexible ? child.fit : FlexFit.loose;
      if (flex > 0) {
        final maxChildExtent = canFlex
            ? (child == lastFlexChild
                ? (freeSpace - allocatedFlexSpace)
                : spacePerFlex * flex)
            : double.infinity;
        double? minChildExtent;
        switch (fit) {
          case FlexFit.tight:
            assert(maxChildExtent < double.infinity);
            minChildExtent = maxChildExtent;
            break;
          case FlexFit.loose:
            minChildExtent = 0.0;
            break;
        }

        BoxConstraints? innerConstraints;
        if (crossAxisAlignment == CrossAxisAlignment.stretch) {
          switch (direction) {
            case Axis.horizontal:
              innerConstraints = BoxConstraints(
                  minWidth: minChildExtent,
                  maxWidth: maxChildExtent,
                  minHeight: constraints.maxHeight,
                  maxHeight: constraints.maxHeight);
              break;
            case Axis.vertical:
              innerConstraints = BoxConstraints(
                  minWidth: constraints.maxWidth,
                  maxWidth: constraints.maxWidth,
                  minHeight: minChildExtent,
                  maxHeight: maxChildExtent);
              break;
          }
        } else {
          switch (direction) {
            case Axis.horizontal:
              innerConstraints = BoxConstraints(
                  minWidth: minChildExtent,
                  maxWidth: maxChildExtent,
                  maxHeight: constraints.maxHeight);
              break;
            case Axis.vertical:
              innerConstraints = BoxConstraints(
                  maxWidth: constraints.maxWidth,
                  minHeight: minChildExtent,
                  maxHeight: maxChildExtent);
              break;
          }
        }
        child.layout(context, innerConstraints, parentUsesSize: true);
        assert(child.box != null);
        final childSize = _getMainSize(child);
        assert(childSize <= maxChildExtent);
        allocatedSize += childSize;
        allocatedFlexSpace += maxChildExtent;
        crossSize = math.max(crossSize, _getCrossSize(child));
      }
    }
  }

  // Align items along the main axis.
  final idealSize = canFlex && mainAxisSize == MainAxisSize.max
      ? maxMainSize
      : allocatedSize;
  double? actualSize;
  double actualSizeDelta;
  late PdfPoint size;
  switch (direction) {
    case Axis.horizontal:
      size = constraints.constrain(PdfPoint(idealSize, crossSize));
      actualSize = size.x;
      crossSize = size.y;
      break;
    case Axis.vertical:
      size = constraints.constrain(PdfPoint(crossSize, idealSize));
      actualSize = size.y;
      crossSize = size.x;
      break;
  }

  box = PdfRect.fromPoints(PdfPoint.zero, size);
  actualSizeDelta = actualSize - allocatedSize;

  final remainingSpace = math.max(0.0, actualSizeDelta);
  double? leadingSpace;
  late double betweenSpace;
  final flipMainAxis = (verticalDirection == VerticalDirection.down &&
          direction == Axis.vertical) ||
      (verticalDirection == VerticalDirection.up &&
          direction == Axis.horizontal);
  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 =
          totalChildren > 1 ? remainingSpace / (totalChildren - 1) : 0.0;
      break;
    case MainAxisAlignment.spaceAround:
      betweenSpace = totalChildren > 0 ? remainingSpace / totalChildren : 0.0;
      leadingSpace = betweenSpace / 2.0;
      break;
    case MainAxisAlignment.spaceEvenly:
      betweenSpace =
          totalChildren > 0 ? remainingSpace / (totalChildren + 1) : 0.0;
      leadingSpace = betweenSpace;
      break;
  }

  // Position elements
  final flipCrossAxis = (verticalDirection == VerticalDirection.down &&
          direction == Axis.horizontal) ||
      (verticalDirection == VerticalDirection.up &&
          direction == Axis.vertical);
  var childMainPosition =
      flipMainAxis ? actualSize - leadingSpace : leadingSpace;

  for (var child
      in children.sublist(_context.firstChild, _context.lastChild)) {
    double? childCrossPosition;
    switch (crossAxisAlignment) {
      case CrossAxisAlignment.start:
        childCrossPosition =
            flipCrossAxis ? crossSize - _getCrossSize(child) : 0.0;
        break;
      case CrossAxisAlignment.end:
        childCrossPosition =
            !flipCrossAxis ? crossSize - _getCrossSize(child) : 0.0;
        break;
      case CrossAxisAlignment.center:
        childCrossPosition = crossSize / 2.0 - _getCrossSize(child) / 2.0;
        break;
      case CrossAxisAlignment.stretch:
        childCrossPosition = 0.0;
        break;
    }

    if (flipMainAxis) {
      childMainPosition -= _getMainSize(child);
    }
    switch (direction) {
      case Axis.horizontal:
        child.box = PdfRect(box!.x + childMainPosition,
            box!.y + childCrossPosition, child.box!.width, child.box!.height);
        break;
      case Axis.vertical:
        child.box = PdfRect(childCrossPosition, childMainPosition,
            child.box!.width, child.box!.height);
        break;
    }
    if (flipMainAxis) {
      childMainPosition -= betweenSpace;
    } else {
      childMainPosition += _getMainSize(child) + betweenSpace;
    }
  }
}