layout method
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()}');
}