getConstraints method
Implementation
BoxConstraints getConstraints() {
CSSDisplay? effectiveDisplay = renderStyle.effectiveDisplay;
bool isDisplayInline = effectiveDisplay == CSSDisplay.inline;
double? minWidth = renderStyle.minWidth.isAuto ? null : renderStyle.minWidth.computedValue;
double? maxWidth = renderStyle.maxWidth.isNone ? null : renderStyle.maxWidth.computedValue;
double? minHeight = renderStyle.minHeight.isAuto ? null : renderStyle.minHeight.computedValue;
double? maxHeight = renderStyle.maxHeight.isNone ? null : renderStyle.maxHeight.computedValue;
// Need to calculated logic content size on every layout.
renderStyle.computeContentBoxLogicalWidth();
renderStyle.computeContentBoxLogicalHeight();
// Width should be not smaller than border and padding in horizontal direction
// when box-sizing is border-box which is only supported.
double minConstraintWidth = renderStyle.effectiveBorderLeftWidth.computedValue +
renderStyle.effectiveBorderRightWidth.computedValue +
renderStyle.paddingLeft.computedValue +
renderStyle.paddingRight.computedValue;
double? parentBoxContentConstraintsWidth;
if (renderStyle.isParentRenderBoxModel() &&
(renderStyle.isSelfRenderLayoutBox() || renderStyle.isSelfRenderWidget())) {
RenderBoxModel parentRenderBoxModel = (renderStyle.getParentRenderStyle()!.attachedRenderBoxModel!);
// Inline-block shrink-to-fit: when the parent is inline-block with auto width,
// do not bound block children by the parent's finite content width. This allows
// the child to compute its own natural width and lets the parent shrink-wrap.
bool parentIsInlineBlockAutoWidth = parentRenderBoxModel.renderStyle.effectiveDisplay == CSSDisplay.inlineBlock &&
parentRenderBoxModel.renderStyle.width.isAuto;
if (parentIsInlineBlockAutoWidth) {
parentBoxContentConstraintsWidth = double.infinity;
} else {
// Prefer the actual layout parent's content constraints if available,
// because CSS parent may not yet have computed contentConstraints during IFC layout.
BoxConstraints? candidate;
if (parent is RenderBoxModel) {
candidate = (parent as RenderBoxModel).contentConstraints;
}
candidate ??= parentRenderBoxModel.contentConstraints;
if (candidate != null) {
// When using CSS parent constraints, deflate with the current element's margins
// to match previous behavior; otherwise use the raw layout parent's constraints.
if (identical(candidate, parentRenderBoxModel.contentConstraints)) {
parentBoxContentConstraintsWidth =
parentRenderBoxModel.renderStyle.deflateMarginConstraints(candidate).maxWidth;
} else {
parentBoxContentConstraintsWidth = candidate.maxWidth;
}
// When inner minimal content size are larger that parent's constraints,
// still use parent constraints but ensure minConstraintWidth is properly handled later
if (parentBoxContentConstraintsWidth < minConstraintWidth) {
// Keep parentBoxContentConstraintsWidth; resolution happens below.
}
}
}
// Flex context adjustments
if (renderStyle.isParentRenderFlexLayout()) {
final RenderFlexLayout flexParent =
renderStyle.getParentRenderStyle()!.attachedRenderBoxModel! as RenderFlexLayout;
// FlexItems with flex:none won't inherit parent box's constraints
if (flexParent.isFlexNone(this)) {
parentBoxContentConstraintsWidth = null;
}
// In a column-direction flex container, a flex item with auto cross-size (width)
// that is not stretched in the cross axis should not inherit the container's
// bounded width during its own constraint computation. Let it shrink-to-fit
// its contents instead (so percentage paddings resolve against the item's
// own width rather than the container width).
final bool isColumn = !CSSFlex.isHorizontalFlexDirection(flexParent.renderStyle.flexDirection);
if (isColumn) {
// Determine if this flex item would be stretched in the cross axis.
final AlignSelf self = renderStyle.alignSelf;
final bool parentStretch = flexParent.renderStyle.alignItems == AlignItems.stretch;
final bool shouldStretch = self == AlignSelf.auto ? parentStretch : self == AlignSelf.stretch;
final bool crossAuto = renderStyle.width.isAuto;
if (!shouldStretch && crossAuto) {
// Do not adopt the parent's bounded width; use intrinsic sizing.
parentBoxContentConstraintsWidth = null;
}
}
}
// For absolutely/fixed positioned elements inside a flex container, avoid collapsing
// their intrinsic width to 0 solely because the flex item has a content constraint
// width of 0 (e.g., auto-width flex item whose only child is out-of-flow).
// In such cases, the containing block still exists, but the abspos box should size
// from its own content rather than the flex item's zero content width.
final bool isAbsOrFixed = renderStyle.position == CSSPositionType.absolute ||
renderStyle.position == CSSPositionType.fixed;
if (isAbsOrFixed &&
renderStyle.width.isAuto &&
renderStyle.isParentRenderFlexLayout() &&
parentBoxContentConstraintsWidth != null &&
parentBoxContentConstraintsWidth == 0) {
parentBoxContentConstraintsWidth = null;
}
} else if (isDisplayInline && parent is RenderFlowLayout) {
// For inline elements inside a flow layout, check if we should inherit parent's constraints
RenderFlowLayout parentFlow = parent as RenderFlowLayout;
// Skip constraint inheritance if parent is a flex item with flex: none (flex-grow: 0, flex-shrink: 0)
if (parentFlow.renderStyle.isParentRenderFlexLayout()) {
RenderFlexLayout flexParent =
parentFlow.renderStyle.getParentRenderStyle()!.attachedRenderBoxModel as RenderFlexLayout;
if (flexParent.isFlexNone(parentFlow)) {
// Don't inherit constraints for flex: none items
parentBoxContentConstraintsWidth = null;
} else {
double parentContentWidth = parentFlow.renderStyle.contentMaxConstraintsWidth;
if (parentContentWidth != double.infinity) {
parentBoxContentConstraintsWidth = parentContentWidth;
}
}
} else {
// Not in a flex context, inherit parent's content width constraint normally
double parentContentWidth = parentFlow.renderStyle.contentMaxConstraintsWidth;
if (parentContentWidth != double.infinity) {
parentBoxContentConstraintsWidth = parentContentWidth;
}
}
}
// CSS abspos width for non-replaced boxes with width:auto follows the
// shrink-to-fit algorithm in CSS 2.1 ยง10.3.7 / CSS Position 3, except when
// both left and right are non-auto (in which case width is solved directly
// from the insets equation).
//
// Approximation: when width:auto and not (left & right both non-auto),
// do not clamp the positioned box by the parent's content max-width.
// Let the box measure to its intrinsic content width (subject to any
// explicit min/max-width), which matches shrink-to-fit in the common
// case where content width <= available width.
final bool _absOrFixedForWidth = renderStyle.position == CSSPositionType.absolute ||
renderStyle.position == CSSPositionType.fixed;
final bool _widthAutoForAbs = renderStyle.width.isAuto;
final bool _bothLRNonAuto = renderStyle.left.isNotAuto && renderStyle.right.isNotAuto;
if (_absOrFixedForWidth &&
!_bothLRNonAuto &&
_widthAutoForAbs &&
!renderStyle.isSelfRenderReplaced() &&
renderStyle.borderBoxLogicalWidth == null &&
parentBoxContentConstraintsWidth != null) {
parentBoxContentConstraintsWidth = null;
}
double maxConstraintWidth =
renderStyle.borderBoxLogicalWidth ?? parentBoxContentConstraintsWidth ?? double.infinity;
// For absolutely/fixed positioned non-replaced elements with both left and right specified
// and width:auto, compute the used border-box width from the containing block at layout time.
// This handles cases where style-tree logical widths are unavailable (e.g., parent inline-block
// with auto width) but the containing block has been measured. See:
// https://www.w3.org/TR/css-position-3/#abs-non-replaced-width
final bool isAbsOrFixed = renderStyle.position == CSSPositionType.absolute ||
renderStyle.position == CSSPositionType.fixed;
if (maxConstraintWidth == double.infinity &&
isAbsOrFixed &&
!renderStyle.isSelfRenderReplaced() &&
renderStyle.width.isAuto &&
renderStyle.left.isNotAuto &&
renderStyle.right.isNotAuto &&
parent is RenderBoxModel) {
final RenderBoxModel cb = parent as RenderBoxModel;
double? parentPaddingBoxWidth;
// Use the parent's content constraints (plus padding) as the containing block width.
// Avoid using parent.size or style-tree logical widths here to prevent feedback loops
// in flex/inline-block shrink-to-fit scenarios.
final BoxConstraints? pcc = cb.contentConstraints;
if (pcc != null && pcc.maxWidth.isFinite) {
parentPaddingBoxWidth = pcc.maxWidth +
cb.renderStyle.paddingLeft.computedValue +
cb.renderStyle.paddingRight.computedValue;
}
if (parentPaddingBoxWidth != null && parentPaddingBoxWidth.isFinite) {
// Solve the horizontal insets equation for the child border-box width.
double solvedBorderBoxWidth = parentPaddingBoxWidth -
renderStyle.left.computedValue -
renderStyle.right.computedValue -
renderStyle.marginLeft.computedValue -
renderStyle.marginRight.computedValue;
// Guard against negative sizes.
solvedBorderBoxWidth = math.max(0, solvedBorderBoxWidth);
// Use a tight width so empty positioned boxes still fill the available space.
minConstraintWidth = solvedBorderBoxWidth;
maxConstraintWidth = solvedBorderBoxWidth;
}
}
// Height should be not smaller than border and padding in vertical direction
// when box-sizing is border-box which is only supported.
double minConstraintHeight = renderStyle.effectiveBorderTopWidth.computedValue +
renderStyle.effectiveBorderBottomWidth.computedValue +
renderStyle.paddingTop.computedValue +
renderStyle.paddingBottom.computedValue;
double maxConstraintHeight = renderStyle.borderBoxLogicalHeight ?? double.infinity;
// // Apply maxHeight constraint if specified
// if (maxHeight != null && maxHeight < maxConstraintHeight) {
// maxConstraintHeight = maxHeight;
// }
if (parent is RenderFlexLayout) {
double? flexBasis = renderStyle.flexBasis == CSSLengthValue.auto ? null : renderStyle.flexBasis?.computedValue;
RenderBoxModel? parentRenderBoxModel = parent as RenderBoxModel?;
// In flex layout, flex basis takes priority over width/height if set.
// Flex-basis cannot be smaller than its content size which happens can not be known
// in constraints apply stage, so flex-basis acts as min-width in constraints apply stage.
if (flexBasis != null) {
if (CSSFlex.isHorizontalFlexDirection(parentRenderBoxModel!.renderStyle.flexDirection)) {
minWidth = minWidth != null ? math.max(flexBasis, minWidth) : flexBasis;
} else {
minHeight = minHeight != null ? math.max(flexBasis, minHeight) : flexBasis;
}
}
}
// Apply min/max width constraints for all display types
if (minWidth != null && !isDisplayInline) {
minConstraintWidth = minConstraintWidth < minWidth ? minWidth : minConstraintWidth;
maxConstraintWidth = maxConstraintWidth < minWidth ? minWidth : maxConstraintWidth;
}
// Apply maxWidth constraint for all elements (including inline)
if (maxWidth != null) {
// Ensure maxConstraintWidth respects maxWidth, but don't reduce minConstraintWidth below border+padding
maxConstraintWidth = maxConstraintWidth > maxWidth ? maxWidth : maxConstraintWidth;
// Only reduce minConstraintWidth if maxWidth is larger than border+padding requirements
double borderPadding = renderStyle.effectiveBorderLeftWidth.computedValue +
renderStyle.effectiveBorderRightWidth.computedValue +
renderStyle.paddingLeft.computedValue +
renderStyle.paddingRight.computedValue;
if (maxWidth >= borderPadding) {
minConstraintWidth = minConstraintWidth > maxWidth ? maxWidth : minConstraintWidth;
}
}
// Apply min/max height constraints when display is not inline
if (!isDisplayInline) {
if (minHeight != null) {
minConstraintHeight = minConstraintHeight < minHeight ? minHeight : minConstraintHeight;
maxConstraintHeight = maxConstraintHeight < minHeight ? minHeight : maxConstraintHeight;
}
if (maxHeight != null) {
minConstraintHeight = minConstraintHeight > maxHeight ? maxHeight : minConstraintHeight;
maxConstraintHeight = maxConstraintHeight > maxHeight ? maxHeight : maxConstraintHeight;
}
}
// Normalize constraints to satisfy Flutter's requirement: min <= max.
// This can happen when the computed minimum width from border/padding
// exceeds the available max width from the parent (e.g., inline-block
// with large horizontal padding inside a narrow container). In such
// cases, cap the minimum to the maximum so constraints are valid.
if (minConstraintWidth > maxConstraintWidth) {
minConstraintWidth = maxConstraintWidth;
}
if (minConstraintHeight > maxConstraintHeight) {
minConstraintHeight = maxConstraintHeight;
}
BoxConstraints constraints = BoxConstraints(
minWidth: minConstraintWidth,
maxWidth: maxConstraintWidth,
minHeight: minConstraintHeight,
maxHeight: maxConstraintHeight,
);
return constraints;
}