calculateBaseline method
void
calculateBaseline(
)
override
Implementation
@override
void calculateBaseline() {
// Cache CSS baselines for this flex container during layout to avoid cross-child baseline computation later.
double? containerBaseline;
CSSDisplay? effectiveDisplay = renderStyle.effectiveDisplay;
bool isDisplayInline = effectiveDisplay != CSSDisplay.block && effectiveDisplay != CSSDisplay.flex;
if (_flexLineBoxMetrics.isEmpty) {
if (isDisplayInline) {
// Inline flex container with no flex items: synthesize baseline from the
// bottom margin edge (consistent with atomic inline-level boxes per CSS2.1).
final double borderBoxHeight = boxSize?.height ?? size.height;
final double marginBottom = renderStyle.marginBottom.computedValue;
containerBaseline = borderBoxHeight + marginBottom;
}
} else {
// If the flex container's main axis differs from the inline axis (e.g. column/column-reverse),
// it participates in baseline alignment as a block container per
// https://www.w3.org/TR/css-flexbox-1/#flex-baselines. Block containers without
// inline content synthesize the baseline from the bottom border edge.
if (!_isHorizontalFlexDirection) {
// Establish baseline from the first flex item on the first line that
// participates in baseline alignment (align-self/align-items: baseline).
final _RunMetrics firstLineMetrics = _flexLineBoxMetrics[0];
final List<_RunChild> firstRunChildren = firstLineMetrics.runChildren.values.toList();
RenderBox? baselineChild;
double? baselineDistance;
RenderBox? fallbackChild;
double? fallbackBaseline;
bool participatesInBaseline(RenderBox candidate) {
// Items with auto margins on the cross axis absorb free space and should not
// be considered for establishing the container's external baseline.
// This mirrors browser behavior in cases like margin-top:auto, where the
// baseline-aligned item no longer anchors the container baseline in IFC.
if (_isChildCrossAxisMarginAutoExist(candidate)) return false;
final AlignSelf self = _getAlignSelf(candidate);
if (self == AlignSelf.baseline) return true;
if (self == AlignSelf.auto && renderStyle.alignItems == AlignItems.baseline) {
return true;
}
return false;
}
for (final _RunChild runChild in firstRunChildren) {
final RenderBox child = runChild.child;
final double? childBaseline = child.getDistanceToBaseline(TextBaseline.alphabetic);
final bool participates = participatesInBaseline(child);
if (participates && baselineChild == null) {
baselineChild = child;
baselineDistance = childBaseline;
if (childBaseline != null) {
break;
}
}
if (childBaseline != null && fallbackChild == null) {
fallbackChild = child;
fallbackBaseline = childBaseline;
}
fallbackChild ??= child;
}
baselineChild ??= fallbackChild;
baselineDistance ??= fallbackBaseline;
if (baselineChild != null) {
if (baselineDistance != null) {
containerBaseline = baselineDistance;
setCssBaselines(first: containerBaseline, last: containerBaseline);
return;
}
// No child provided a baseline; fall back to block container behavior
// (distance from border-box top to the bottom border edge).
}
final double borderBoxHeight = boxSize?.height ?? size.height;
// For inline flex containers, include bottom margin to synthesize an
// external baseline from the bottom margin edge.
if (isDisplayInline) {
containerBaseline = borderBoxHeight + renderStyle.marginBottom.computedValue;
} else {
containerBaseline = borderBoxHeight;
}
setCssBaselines(first: containerBaseline, last: containerBaseline);
return;
}
// Row-direction (horizontal main axis): per CSS Flexbox §10.8 and
// Baseline Alignment in Flexbox, the container’s baseline is taken from
// the first flex item on the first line that participates in baseline
// alignment (align-self: baseline or align-items: baseline). If none
// participate, fall back to the first item with a baseline; if no item
// exposes a baseline, synthesize from the bottom border edge.
final _RunMetrics firstLineMetrics = _flexLineBoxMetrics[0];
final List<_RunChild> firstRunChildren = firstLineMetrics.runChildren.values.toList();
if (firstRunChildren.isNotEmpty) {
RenderBox? baselineChild;
double? baselineDistance; // distance from child's border-top to its baseline
RenderBox? fallbackChild;
double? fallbackBaseline;
bool participatesInBaseline(RenderBox candidate) {
final AlignSelf self = _getAlignSelf(candidate);
if (self == AlignSelf.baseline) return true;
if (self == AlignSelf.auto && renderStyle.alignItems == AlignItems.baseline) {
return true;
}
return false;
}
for (final _RunChild runChild in firstRunChildren) {
final RenderBox child = runChild.child;
final double? childBaseline = child.getDistanceToBaseline(TextBaseline.alphabetic);
// Compute baseline participation (inline here for robust wrapper handling)
RenderBoxModel? styleBox;
if (child is RenderBoxModel) {
styleBox = child;
} else if (child is RenderEventListener) {
styleBox = child.child as RenderBoxModel?;
} else if (child is RenderPositionPlaceholder) {
styleBox = child.positioned;
}
bool hasCrossAuto = false;
double mt = 0, mb = 0; bool mtAuto = false, mbAuto = false;
if (styleBox != null) {
final s = styleBox.renderStyle;
mt = s.marginTop.computedValue;
mb = s.marginBottom.computedValue;
mtAuto = s.marginTop.isAuto;
mbAuto = s.marginBottom.isAuto;
hasCrossAuto = _isHorizontalFlexDirection
? (mtAuto || mbAuto)
: (s.marginLeft.isAuto || s.marginRight.isAuto);
}
AlignSelf self = _getAlignSelf(child);
final bool participates = (!hasCrossAuto) &&
(self == AlignSelf.baseline || (self == AlignSelf.auto && renderStyle.alignItems == AlignItems.baseline));
double dy = 0;
if (child.parentData is RenderLayoutParentData) {
dy = (child.parentData as RenderLayoutParentData).offset.dy;
}
if (participates && baselineChild == null) {
baselineChild = child;
baselineDistance = childBaseline;
if (childBaseline != null) {
break;
}
}
if (childBaseline != null && fallbackChild == null) {
fallbackChild = child;
fallbackBaseline = childBaseline;
}
fallbackChild ??= child;
}
// Prefer the first baseline-participating child (excluding cross-axis auto margins);
// otherwise fall back to the first child that exposes a baseline; otherwise the first item.
final RenderBox? chosen = baselineChild ?? fallbackChild;
final double? chosenBaseline = baselineChild != null ? baselineDistance : fallbackBaseline;
if (chosen != null) {
if (chosenBaseline != null) {
final RenderLayoutParentData pd = chosen.parentData as RenderLayoutParentData;
double dy = pd.offset.dy;
if (chosen is RenderBoxModel) {
final Offset? rel = CSSPositionedLayout.getRelativeOffset(chosen.renderStyle);
if (rel != null) dy -= rel.dy;
}
containerBaseline = chosenBaseline + dy;
} else {
// Chosen item has no baseline; synthesize from container bottom edge.
final double borderBoxHeight = boxSize?.height ?? size.height;
// If inline-level (inline-flex), synthesize from bottom margin edge.
if (isDisplayInline) {
containerBaseline = borderBoxHeight + renderStyle.marginBottom.computedValue;
} else {
containerBaseline = borderBoxHeight;
}
}
}
}
}
setCssBaselines(first: containerBaseline, last: containerBaseline);
}