calculateBaseline method

  1. @override
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);
}