extendMaxScrollableSize method

void extendMaxScrollableSize(
  1. RenderBoxModel child
)

Extend max scrollable size of renderBoxModel by offset of positioned child, get the max scrollable size of children of normal flow and single positioned child.

Implementation

void extendMaxScrollableSize(RenderBoxModel child) {
  Size? childScrollableSize;
  RenderStyle childRenderStyle = child.renderStyle;
  CSSOverflowType overflowX = childRenderStyle.effectiveOverflowX;
  CSSOverflowType overflowY = childRenderStyle.effectiveOverflowY;
  // Only non scroll container need to use scrollable size, otherwise use its own size
  if (overflowX == CSSOverflowType.visible && overflowY == CSSOverflowType.visible) {
    childScrollableSize = child.scrollableSize;
  } else {
    childScrollableSize = child.boxSize;
  }

  Matrix4? transform = (childRenderStyle as CSSRenderStyle).transformMatrix;

  // Determine container content box size for positioned computations.
  double containerContentWidth = _contentSize!.width;
  double containerContentHeight = _contentSize!.height;
  if (parent is RenderBoxModel) {
    final RenderBoxModel p = parent as RenderBoxModel;
    if (p.widthSizeType == BoxSizeType.specified) {
      containerContentWidth = p.renderStyle.width.computedValue -
          p.renderStyle.effectiveBorderLeftWidth.computedValue -
          p.renderStyle.effectiveBorderRightWidth.computedValue;
    }
    if (p.heightSizeType == BoxSizeType.specified) {
      containerContentHeight = p.renderStyle.height.computedValue -
          p.renderStyle.effectiveBorderTopWidth.computedValue -
          p.renderStyle.effectiveBorderBottomWidth.computedValue;
    }
  }

  // Compute positioned box edges relative to the content (padding) box origin.
  final Size childSize = childScrollableSize!;
  double childLeft;
  if (childRenderStyle.left.isNotAuto) {
    childLeft = childRenderStyle.left.computedValue;
  } else if (childRenderStyle.right.isNotAuto) {
    childLeft = containerContentWidth - childSize.width - childRenderStyle.right.computedValue;
  } else {
    childLeft = 0;
  }
  double childTop;
  if (childRenderStyle.top.isNotAuto) {
    childTop = childRenderStyle.top.computedValue;
  } else if (childRenderStyle.bottom.isNotAuto) {
    childTop = containerContentHeight - childSize.height - childRenderStyle.bottom.computedValue;
  } else {
    childTop = 0;
  }

  if (transform != null) {
    childLeft += transform.getTranslation()[0];
    childTop += transform.getTranslation()[1];
  }

  final double childRight = childLeft + childSize.width;
  final double childBottom = childTop + childSize.height;

  // Extend scroll area when the positioned box reaches or crosses the
  // scrollport (padding box) boundary.
  // For LTR, treat boxes that start exactly at the trailing edge as contributing
  // (<=) so users can scroll to reveal them. For RTL, keep strict (<) to avoid
  // shifting initial visual alignment for cases like right:-N content.
  final bool parentIsRTL = renderStyle.direction == TextDirection.rtl;
  final bool intersectsH =
      childRight > 0 && (parentIsRTL ? (childLeft < containerContentWidth) : (childLeft <= containerContentWidth));
  final bool intersectsV = childBottom > 0 && childTop <= containerContentHeight;

  double maxScrollableX = scrollableSize.width;
  double maxScrollableY = scrollableSize.height;

  if (intersectsH) {
    maxScrollableX = math.max(maxScrollableX, math.max(containerContentWidth, childRight));
  }
  // Only extend vertical scroll when the positioned box also intersects (or
  // reaches) the scrollport horizontally. Purely off-axis content should not
  // create scroll in the other axis.
  if (intersectsV && intersectsH) {
    maxScrollableY = math.max(maxScrollableY, math.max(containerContentHeight, childBottom));
  }

  scrollableSize = Size(maxScrollableX, maxScrollableY);
}