layout method

  1. @override
void layout(
  1. BoxConstraints constraints
)
override

Implementation

@override
void layout(BoxConstraints constraints) {
  final span = TuiTrace.begin(
    'RenderRow.layout',
    tag: TraceTag.layout,
    extra: 'children=${children.length}',
  );
  super.layout(constraints);
  var width = 0.0;
  var height = 0.0;

  final isStretch = crossAxisAlignment == RenderCrossAxisAlignment.stretch;

  // --- Two-pass flex layout (mirrors Flutter's RenderFlex) ---
  //
  // Pass 1: Layout non-flex children to determine how much space is left
  // for flex children.  Use loose cross constraints initially; stretch
  // is applied in a final pass once the resolved cross size is known.

  final flexData = children.map(_flexDataFor).toList();
  final totalFlex = flexData.fold<int>(0, (sum, f) => sum + f.flex);
  final gapTotal = children.length > 1
      ? gap * (children.length - 1).toDouble()
      : 0.0;

  var nonFlexWidth = 0.0;
  for (var i = 0; i < children.length; i++) {
    if (flexData[i].flex > 0) continue;
    final childConstraints = BoxConstraints(
      minWidth: 0,
      maxWidth: constraints.hasBoundedWidth
          ? double.infinity
          : constraints.maxWidth,
      minHeight: 0,
      maxHeight: constraints.maxHeight,
    );
    if (_shouldLayoutChild(children[i], childConstraints)) {
      children[i].layout(childConstraints);
    }
    nonFlexWidth += children[i].size.width;
    height = math.max(height, children[i].size.height);
  }

  // Pass 2: Distribute remaining space among flex children.
  if (totalFlex > 0 && constraints.hasBoundedWidth) {
    final available = math.max(
      0.0,
      constraints.maxWidth - nonFlexWidth - gapTotal,
    );
    for (var i = 0; i < children.length; i++) {
      final data = flexData[i];
      if (data.flex <= 0) continue;
      final alloc = (available * data.flex) / totalFlex;
      final allocInt = alloc.floorToDouble();
      final childConstraints = data.fit == RenderFlexFit.tight
          ? BoxConstraints(
              minWidth: allocInt,
              maxWidth: allocInt,
              minHeight: 0,
              maxHeight: constraints.maxHeight,
            )
          : BoxConstraints(
              minWidth: 0,
              maxWidth: allocInt,
              minHeight: 0,
              maxHeight: constraints.maxHeight,
            );
      if (_shouldLayoutChild(children[i], childConstraints)) {
        children[i].layout(childConstraints);
      }
      height = math.max(height, children[i].size.height);
    }
  } else {
    // No flex or unbounded — layout flex children with loose constraints.
    for (var i = 0; i < children.length; i++) {
      if (flexData[i].flex <= 0) continue;
      final childConstraints = BoxConstraints(
        minWidth: 0,
        maxWidth: constraints.hasBoundedWidth
            ? double.infinity
            : constraints.maxWidth,
        minHeight: 0,
        maxHeight: constraints.maxHeight,
      );
      if (_shouldLayoutChild(children[i], childConstraints)) {
        children[i].layout(childConstraints);
      }
      height = math.max(height, children[i].size.height);
    }
  }

  // Resolve cross size: max(constraints.minHeight, maxChildHeight).
  // This matches Flutter's RenderFlex cross-axis resolution.
  final crossSize = math.max(constraints.minHeight, height);

  // Pass 3 (stretch only): Re-layout children that need to match the
  // resolved cross size.  This is necessary because the initial passes
  // used loose cross constraints to discover the natural cross extent.
  if (isStretch) {
    for (var i = 0; i < children.length; i++) {
      final child = children[i];
      if (child.size.height == crossSize) continue;
      final prevConstraints = child.constraints;
      final stretchConstraints = BoxConstraints(
        minWidth: prevConstraints.minWidth,
        maxWidth: prevConstraints.maxWidth,
        minHeight: crossSize,
        maxHeight: crossSize,
      );
      if (_shouldLayoutChild(child, stretchConstraints)) {
        child.layout(stretchConstraints);
      }
    }
  }

  width = children.fold<double>(0, (sum, c) => sum + c.size.width) + gapTotal;

  final contentWidth = width;
  final contentHeight = isStretch ? crossSize : height;

  final resolvedWidth = mainAxisSize == RenderMainAxisSize.max
      ? (mainAxisExtent?.toDouble() ??
            (constraints.hasBoundedWidth
                ? constraints.maxWidth
                : contentWidth))
      : contentWidth;
  final resolvedHeight = crossAxisExtent?.toDouble() ?? contentHeight;

  size = constraints.constrain(Size(resolvedWidth, resolvedHeight));

  // Compute child offsets matching the paint layout logic.
  _computeChildOffsets();
  span.end(extra: 'size=${size.width.toInt()}x${size.height.toInt()}');
}