describeOverflowSemantics method
Implementation
void describeOverflowSemantics(SemanticsConfiguration config) {
final CSSOverflowType overflowX = renderStyle.effectiveOverflowX;
final CSSOverflowType overflowY = renderStyle.effectiveOverflowY;
final bool xScrollable =
(overflowX == CSSOverflowType.scroll || overflowX == CSSOverflowType.auto) && scrollOffsetX != null;
final bool yScrollable =
(overflowY == CSSOverflowType.scroll || overflowY == CSSOverflowType.auto) && scrollOffsetY != null;
if (!xScrollable && !yScrollable) {
return;
}
// Mirror Flutter's _RenderScrollSemantics: scrolling regions are semantic
// boundaries with explicit child nodes, and expose scroll metrics.
config.isSemanticBoundary = true;
config.explicitChildNodes = true;
config.hasImplicitScrolling = true;
final Size? viewport = _viewportSize;
final Size? content = _scrollableSize;
if (viewport == null || content == null) {
return;
}
final double maxX = math.max(0.0, content.width - viewport.width);
final double maxY = math.max(0.0, content.height - viewport.height);
// Expose primary-axis scroll metrics (prefer vertical when available).
if (yScrollable) {
config.scrollExtentMin = 0.0;
config.scrollExtentMax = maxY;
config.scrollPosition = scrollTop.clamp(0.0, maxY).toDouble();
} else if (xScrollable) {
config.scrollExtentMin = 0.0;
config.scrollExtentMax = maxX;
config.scrollPosition = scrollLeft.clamp(0.0, maxX).toDouble();
}
// Best-effort child count: used by some platforms to provide "x of y".
try {
config.scrollChildCount = renderStyle.target.childNodes.length;
} catch (_) {}
final bool canScroll = (yScrollable && maxY > 0.0) || (xScrollable && maxX > 0.0);
if (!canScroll) {
return;
}
// Semantic scroll-to-offset for implicit scrolling.
config.onScrollToOffset = (Offset targetOffset) {
if (yScrollable && scrollOffsetY != null) {
final double targetY = targetOffset.dy.clamp(0.0, maxY).toDouble();
scrollOffsetY!.jumpTo(targetY);
}
if (xScrollable && scrollOffsetX != null) {
final double targetX = targetOffset.dx.clamp(0.0, maxX).toDouble();
scrollOffsetX!.jumpTo(targetX);
}
};
// Semantic scroll actions (RenderSemanticsGestureHandler-like behavior).
if (yScrollable && scrollOffsetY != null && maxY > 0.0 && hasSize) {
config.onScrollUp = () {
final double delta = size.height * -_kSemanticsScrollFactor;
final double next = (scrollTop + delta).clamp(0.0, maxY).toDouble();
scrollOffsetY!.jumpTo(next);
};
config.onScrollDown = () {
final double delta = size.height * _kSemanticsScrollFactor;
final double next = (scrollTop + delta).clamp(0.0, maxY).toDouble();
scrollOffsetY!.jumpTo(next);
};
}
if (xScrollable && scrollOffsetX != null && maxX > 0.0 && hasSize) {
config.onScrollLeft = () {
final double delta = size.width * -_kSemanticsScrollFactor;
final double next = (scrollLeft + delta).clamp(0.0, maxX).toDouble();
scrollOffsetX!.jumpTo(next);
};
config.onScrollRight = () {
final double delta = size.width * _kSemanticsScrollFactor;
final double next = (scrollLeft + delta).clamp(0.0, maxX).toDouble();
scrollOffsetX!.jumpTo(next);
};
}
}