layout method

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

Implementation

@override
void layout(BoxConstraints constraints) {
  final span = TuiTrace.begin(
    'RenderContainer.layout',
    tag: TraceTag.layout,
    extra: 'w=$width h=$height',
  );
  super.layout(constraints);

  // Compute the decoration overhead (padding + border + margin) so we can
  // deflate child constraints — matching Flutter's RenderPadding behaviour.
  final boxDec = decoration is BoxDecoration
      ? decoration as BoxDecoration
      : null;
  final bdr = boxDec?.border;
  final bdrLeft = (bdr != null && bdr.isVisible) ? bdr.getLeftSize() : 0;
  final bdrRight = (bdr != null && bdr.isVisible) ? bdr.getRightSize() : 0;
  final bdrTop = (bdr != null && bdr.isVisible) ? bdr.getTopSize() : 0;
  final bdrBottom = (bdr != null && bdr.isVisible) ? bdr.getBottomSize() : 0;
  final bdrH = bdrLeft + bdrRight;
  final bdrV = bdrTop + bdrBottom;
  final padLeft = _roundClamp(padding?.left ?? 0);
  final padRight = _roundClamp(padding?.right ?? 0);
  final padTop = _roundClamp(padding?.top ?? 0);
  final padBottom = _roundClamp(padding?.bottom ?? 0);
  final padH = padLeft + padRight;
  final padV = padTop + padBottom;
  final mrgLeft = _roundClamp(margin?.left ?? 0);
  final mrgRight = _roundClamp(margin?.right ?? 0);
  final mrgTop = _roundClamp(margin?.top ?? 0);
  final mrgBottom = _roundClamp(margin?.bottom ?? 0);
  final mrgH = mrgLeft + mrgRight;
  final mrgV = mrgTop + mrgBottom;

  // Overhead inside the container box that reduces space available to child.
  // Margin is outside the container and must not constrain the child.
  final innerOverheadH = padH + bdrH;
  final innerOverheadV = padV + bdrV;

  var childConstraints = constraints;
  if (width != null || height != null) {
    childConstraints = BoxConstraints(
      minWidth: math.max(
        0,
        (width?.toDouble() ?? constraints.minWidth) - innerOverheadH,
      ),
      maxWidth: math.max(
        0,
        (width?.toDouble() ?? constraints.maxWidth) - innerOverheadH,
      ),
      minHeight: math.max(
        0,
        (height?.toDouble() ?? constraints.minHeight) - innerOverheadV,
      ),
      maxHeight: math.max(
        0,
        (height?.toDouble() ?? constraints.maxHeight) - innerOverheadV,
      ),
    );
  } else if (alignment != null) {
    // Alignment is set — loosen min constraints so the child can size
    // naturally and then be positioned within the container.  This
    // matches Flutter's Container behaviour with alignment.
    childConstraints = BoxConstraints(
      minWidth: 0,
      maxWidth: math.max(0, constraints.maxWidth - innerOverheadH),
      minHeight: 0,
      maxHeight: math.max(0, constraints.maxHeight - innerOverheadV),
    );
  } else {
    // No explicit size, no alignment — propagate parent constraints
    // through (deflated by padding/border/margin overhead) so that
    // children see the same tightness the parent intended.  This
    // matches Flutter behaviour where a Container with only `color`
    // (or padding) is constraint-transparent.
    childConstraints = BoxConstraints(
      minWidth: math.max(0, constraints.minWidth - innerOverheadH),
      maxWidth: math.max(0, constraints.maxWidth - innerOverheadH),
      minHeight: math.max(0, constraints.minHeight - innerOverheadV),
      maxHeight: math.max(0, constraints.maxHeight - innerOverheadV),
    );
  }
  _child?.layout(childConstraints);
  final contentW = _child?.size.width.toInt() ?? 0;
  final contentH = _child?.size.height.toInt() ?? 0;

  // Compute the natural total size that _renderContainerContent would
  // produce when width/height are null, accounting for padding, border,
  // and margin — mirroring the same arithmetic in _renderContainerContent.
  final naturalInnerW = (width != null)
      ? _resolveDimension(width)!
      : (contentW + padH + bdrH);
  final naturalInnerH = (height != null)
      ? _resolveDimension(height)!
      : (contentH + padV + bdrV);
  final naturalTotalW = naturalInnerW + mrgH;
  final naturalTotalH = naturalInnerH + mrgV;

  final constrained = constraints.constrain(
    Size(naturalTotalW.toDouble(), naturalTotalH.toDouble()),
  );

  // Ensure the render dimensions match the constrained size so the
  // Canvas output never exceeds the constraints.  This handles both
  // when constraints force a *larger* size (expansion) and when they
  // force a *smaller* size (clamping) than the natural dimensions.
  num? renderWidth = width;
  num? renderHeight = height;
  if (width == null && constrained.width != naturalTotalW) {
    renderWidth = constrained.width - mrgH;
  }
  if (height == null && constrained.height != naturalTotalH) {
    renderHeight = constrained.height - mrgV;
  }

  _resolvedWidth = renderWidth;
  _resolvedHeight = renderHeight;
  size = constraints.constrain(
    Size(
      (renderWidth != null
              ? _resolveDimension(renderWidth) ?? 0
              : naturalInnerW) +
          mrgH.toDouble(),
      (renderHeight != null
              ? _resolveDimension(renderHeight) ?? 0
              : naturalInnerH) +
          mrgV.toDouble(),
    ),
  );

  // Set child offset to match where _renderContainerContent places the
  // content on the canvas: margin + border + padding + alignment.
  if (_child != null) {
    final resolvedW = _resolveDimension(renderWidth);
    final resolvedH = _resolveDimension(renderHeight);

    final resolvedAlign = alignment == null
        ? align
        : _horizontalFromAlignment(alignment!);
    final resolvedVertical = alignment == null
        ? verticalAlign
        : _verticalFromAlignment(alignment!);

    final availW = resolvedW != null
        ? math.max(0, resolvedW - padLeft - padRight - bdrH)
        : 0;
    final availH = resolvedH != null
        ? math.max(0, resolvedH - padTop - padBottom - bdrV)
        : 0;

    final alignedX = resolvedW != null
        ? _offsetForHorizontal(resolvedAlign, availW, contentW)
        : 0;
    final alignedY = resolvedH != null
        ? _offsetForVertical(resolvedVertical, availH, contentH)
        : 0;

    _child!.offset = Offset(
      (mrgLeft + bdrLeft + padLeft + alignedX).toDouble(),
      (mrgTop + bdrTop + padTop + alignedY).toDouble(),
    );
  }
  span.end(extra: 'size=${size.width.toInt()}x${size.height.toInt()}');
}