getContentSize method

Size getContentSize({
  1. required double contentWidth,
  2. required double contentHeight,
})

Common layout content size (including flow and flexbox layout) calculation logic

Implementation

Size getContentSize({
  required double contentWidth,
  required double contentHeight,
}) {
  try {
    // Keep for future diagnostic hooks (no-op by default).
    final tag = renderStyle.target.tagName.toLowerCase();
    final disp = renderStyle.effectiveDisplay;
    final pType = parent?.runtimeType.toString() ?? 'null';
    final _ = '[BoxSize] <$tag> getContentSize in='
        '${contentWidth.toStringAsFixed(2)}×${contentHeight.toStringAsFixed(2)} '
        'display=$disp parent=$pType';
  } catch (_) {}
  double finalContentWidth = contentWidth;
  double finalContentHeight = contentHeight;

  // Size which is specified by sizing styles
  double? specifiedContentWidth = renderStyle.contentBoxLogicalWidth;
  double? specifiedContentHeight = renderStyle.contentBoxLogicalHeight;

  // Margin negative will set element which is static && not set width, size bigger
  double? marginLeft = renderStyle.marginLeft.computedValue;
  double? marginRight = renderStyle.marginRight.computedValue;
  double? marginAddSizeLeft = 0;
  double? marginAddSizeRight = 0;
  if (isNegativeMarginChangeHSize) {
    marginAddSizeRight = marginLeft < 0 ? -marginLeft : 0;
    marginAddSizeLeft = marginRight < 0 ? -marginRight : 0;
  }
  // Flex items: when flex-basis is specified (not 'auto'), it overrides the
  // main-size property (width/height) for the base size per CSS Flexbox.
  // Use the resolved flex-basis as the specified content size on the main axis
  // so the item measures to its base size during intrinsic layout.
  if (renderStyle.isParentRenderFlexLayout()) {
    final CSSRenderStyle? parentRenderStyle = renderStyle.getParentRenderStyle();
    final CSSLengthValue? flexBasisLV = renderStyle.flexBasis;
    final bool isFlexBasisContent = flexBasisLV?.type == CSSLengthType.CONTENT;
    final double? flexBasis = (flexBasisLV == null || flexBasisLV == CSSLengthValue.auto || isFlexBasisContent)
        ? null
        : flexBasisLV.computedValue;

    if (flexBasis != null && parentRenderStyle != null) {
      // Determine main-axis orientation with writing-mode awareness.
      final CSSWritingMode wm = (parentRenderStyle is CSSRenderStyle)
          ? parentRenderStyle.writingMode
          : CSSWritingMode.horizontalTb;
      final bool inlineIsHorizontal = (wm == CSSWritingMode.horizontalTb);
      final bool parentRow = parentRenderStyle.flexDirection == FlexDirection.row ||
          parentRenderStyle.flexDirection == FlexDirection.rowReverse;
      final bool isMainAxisHorizontal = parentRow ? inlineIsHorizontal : !inlineIsHorizontal;
      final bool isParentMainDefinite = isMainAxisHorizontal
          ? parentRenderStyle.contentBoxLogicalWidth != null
          : parentRenderStyle.contentBoxLogicalHeight != null;
      final bool isPctBasis = flexBasisLV!.type == CSSLengthType.PERCENTAGE;

      // Follow CSS spec: percentage flex-basis resolves against the flex container's main size;
      // if that size is indefinite, the used value is 'content'. In that case, do not override
      // the specified content size here—let content determine sizing.
      if (isPctBasis && !isParentMainDefinite) {
        // Skip overriding specified content size.
      } else if (flexBasis > 0) {
        // Only apply positive, resolvable flex-basis to content sizing here.
        if (isMainAxisHorizontal) {
          if (!hasOverrideContentLogicalWidth) {
            specifiedContentWidth = _getContentWidth(flexBasis);
          }
        } else {
          if (!hasOverrideContentLogicalHeight) {
            specifiedContentHeight = _getContentHeight(flexBasis);
          }
        }
      } else {
        // Non-positive flex-basis should not clamp intrinsic content to zero here.
      }
    }
  }

  // If an explicit content width is specified via CSS (width/min/max already
  // resolved above), it should determine the used content width. Do not
  // auto-expand to accommodate measured content here — overflow handling
  // should deal with larger content per CSS. Using max() causes blocks inside
  // unbounded containers (e.g. horizontal slivers) to incorrectly expand to
  // ancestor viewport widths instead of honoring the specified width.
  if (specifiedContentWidth != null) {
    finalContentWidth = specifiedContentWidth;
  }
  if (parent is RenderFlexLayout && marginAddSizeLeft > 0 && marginAddSizeRight > 0 ||
      parent is RenderFlowLayout && (marginAddSizeRight > 0 || marginAddSizeLeft > 0)) {
    finalContentWidth += marginAddSizeLeft;
    finalContentWidth += marginAddSizeRight;
  }
  // Same rule for height: honor the specified content height if provided
  // rather than expanding to measured content height here.
  if (specifiedContentHeight != null) {
    finalContentHeight = specifiedContentHeight;
  }

  CSSDisplay? effectiveDisplay = renderStyle.effectiveDisplay;
  bool isInlineBlock = effectiveDisplay == CSSDisplay.inlineBlock;
  bool isNotInline = effectiveDisplay != CSSDisplay.inline;
  double? width = renderStyle.width.isAuto ? null : renderStyle.width.computedValue;
  double? height = renderStyle.height.isAuto ? null : renderStyle.height.computedValue;
  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;

  // Constrain to min-width or max-width if width not exists.
  if (isInlineBlock && maxWidth != null && width == null) {
    double maxContentWidth = _getContentWidth(maxWidth);
    finalContentWidth = finalContentWidth > maxContentWidth ? maxContentWidth : finalContentWidth;
  } else if (isInlineBlock && minWidth != null && width == null) {
    double minContentWidth = _getContentWidth(minWidth);
    finalContentWidth = finalContentWidth < minContentWidth ? minContentWidth : finalContentWidth;
  }

  // Constrain to min-height or max-height if height not exists.
  if (isNotInline && maxHeight != null && height == null) {
    double maxContentHeight = _getContentHeight(maxHeight);
    finalContentHeight = finalContentHeight > maxContentHeight ? maxContentHeight : finalContentHeight;
  } else if (isNotInline && minHeight != null && height == null) {
    double minContentHeight = _getContentHeight(minHeight);
    finalContentHeight = finalContentHeight < minContentHeight ? minContentHeight : finalContentHeight;
  }

  Size finalContentSize = Size(finalContentWidth, finalContentHeight);

  try {
    // Keep for future diagnostic hooks (no-op by default).
    final tag = renderStyle.target.tagName.toLowerCase();
    final paddL = renderStyle.paddingLeft.computedValue;
    final paddR = renderStyle.paddingRight.computedValue;
    final bordL = renderStyle.effectiveBorderLeftWidth.computedValue;
    final bordR = renderStyle.effectiveBorderRightWidth.computedValue;
    final _ = '[BoxSize] <$tag> getContentSize out='
        '${finalContentSize.width.toStringAsFixed(2)}×${finalContentSize.height.toStringAsFixed(2)} '
        'padH=${(paddL + paddR).toStringAsFixed(2)} borderH=${(bordL + bordR).toStringAsFixed(2)}';
  } catch (_) {}
  return finalContentSize;
}