performLayout method

  1. @override
Size performLayout(
  1. BoxConstraints constraints
)
override

Hook for subclasses to perform layout within the given constraints.

Implementation

@override
Size performLayout(BoxConstraints constraints) {
  var maxW = 0;
  var maxH = 0;
  var hasNonPositioned = false;

  // 1. First pass: lay out non-positioned children to determine the Stack's size.
  final childConstraints = BoxConstraints(
    minWidth: 0,
    maxWidth: constraints.maxWidth < 0 ? 0 : constraints.maxWidth,
    minHeight: 0,
    maxHeight: constraints.maxHeight < 0 ? 0 : constraints.maxHeight,
  );
  for (var i = 0; i < childElements.length; i++) {
    final childEl = childElements[i];
    final childWidget = childEl.widget;

    if (childWidget is! Positioned) {
      hasNonPositioned = true;
      childEl.relativeOffset = Offset.zero;
      final childSize = childEl.layout(childConstraints);
      if (childSize.width > maxW) maxW = childSize.width;
      if (childSize.height > maxH) maxH = childSize.height;
    }
  }

  // 2. Resolve the final size of the Stack.
  int resolvedW;
  int resolvedH;
  if (hasNonPositioned) {
    final resolvedSize = constraints.constrain(Size(maxW, maxH));
    resolvedW = resolvedSize.width;
    resolvedH = resolvedSize.height;
  } else {
    resolvedW = constraints.maxWidth == BoxConstraints.infinity
        ? constraints.minWidth
        : constraints.maxWidth;
    resolvedH = constraints.maxHeight == BoxConstraints.infinity
        ? constraints.minHeight
        : constraints.maxHeight;
  }

  // 3. Second pass: lay out and position Positioned children using the resolved Stack size.
  for (var i = 0; i < childElements.length; i++) {
    final childEl = childElements[i];
    final childWidget = childEl.widget;

    if (childWidget is Positioned) {
      final (minChildW, maxChildW, childX) = switch ((
        childWidget.isCentered,
        childWidget.left,
        childWidget.right,
        childWidget.width,
      )) {
        (true, _, _, int w) => (w, w, null as int?),
        (true, _, _, null) => (0, resolvedW, null as int?),
        (false, int left, int right, _) => (
          (resolvedW - left - right) < 0 ? 0 : resolvedW - left - right,
          (resolvedW - left - right) < 0 ? 0 : resolvedW - left - right,
          left as int?,
        ),
        (false, int left, null, int w) => (w, w, left as int?),
        (false, null, int right, int w) => (w, w, resolvedW - right - w),
        (false, int left, null, null) => (
          0,
          (resolvedW - left) < 0 ? 0 : resolvedW - left,
          left as int?,
        ),
        (false, null, int right, null) => (
          0,
          (resolvedW - right) < 0 ? 0 : resolvedW - right,
          null as int?,
        ),
        (false, null, null, int w) => (w, w, 0 as int?),
        (false, null, null, null) => (0, resolvedW, 0 as int?),
      };

      final (minChildH, maxChildH, childY) = switch ((
        childWidget.isCentered,
        childWidget.top,
        childWidget.bottom,
        childWidget.height,
      )) {
        (true, _, _, int h) => (h, h, null as int?),
        (true, _, _, null) => (0, resolvedH, null as int?),
        (false, int top, int bottom, _) => (
          (resolvedH - top - bottom) < 0 ? 0 : resolvedH - top - bottom,
          (resolvedH - top - bottom) < 0 ? 0 : resolvedH - top - bottom,
          top as int?,
        ),
        (false, int top, null, int h) => (h, h, top as int?),
        (false, null, int bottom, int h) => (h, h, resolvedH - bottom - h),
        (false, int top, null, null) => (
          0,
          (resolvedH - top) < 0 ? 0 : resolvedH - top,
          top as int?,
        ),
        (false, null, int bottom, null) => (
          0,
          (resolvedH - bottom) < 0 ? 0 : resolvedH - bottom,
          null as int?,
        ),
        (false, null, null, int h) => (h, h, 0 as int?),
        (false, null, null, null) => (0, resolvedH, 0 as int?),
      };

      final childSize = childEl.layout(
        BoxConstraints(
          minWidth: minChildW,
          maxWidth: maxChildW,
          minHeight: minChildH,
          maxHeight: maxChildH,
        ),
      );

      final finalX = childWidget.isCentered
          ? (resolvedW - childSize.width) ~/ 2
          : (childX ?? resolvedW - childWidget.right! - childSize.width);

      final finalY = childWidget.isCentered
          ? (resolvedH - childSize.height) ~/ 2
          : (childY ?? resolvedH - childWidget.bottom! - childSize.height);

      childEl.relativeOffset = Offset(finalX, finalY);
    }
  }

  return Size(resolvedW, resolvedH);
}