getOffsetToReveal method
- RenderObject target,
- double alignment, {
- Rect? rect,
- Axis? axis,
Returns the offset that would be needed to reveal the target
RenderObject.
This is used by RenderViewportBase.showInViewport, which is itself used by RenderObject.showOnScreen for RenderViewportBase, which is in turn used by the semantics system to implement scrolling for accessibility tools.
The optional rect parameter describes which area of that target object
should be revealed in the viewport. If rect is null, the entire
target RenderObject (as defined by its RenderObject.paintBounds)
will be revealed. If rect is provided it has to be given in the
coordinate system of the target object.
The alignment argument describes where the target should be positioned
after applying the returned offset. If alignment is 0.0, the child must
be positioned as close to the leading edge of the viewport as possible. If
alignment is 1.0, the child must be positioned as close to the trailing
edge of the viewport as possible. If alignment is 0.5, the child must be
positioned as close to the center of the viewport as possible.
The target might not be a direct child of this viewport but it must be a
descendant of the viewport. Other viewports in between this viewport and
the target will not be adjusted.
This method assumes that the content of the viewport moves linearly, i.e.
when the offset of the viewport is changed by x then target also moves
by x within the viewport.
The optional Axis is used by
RenderTwoDimensionalViewport.getOffsetToReveal to
determine which of the two axes to compute an offset for. One dimensional
subclasses like RenderViewportBase and RenderListWheelViewport
will ignore the axis value if provided, since there is only one Axis.
If the axis is omitted when called on RenderTwoDimensionalViewport,
the RenderTwoDimensionalViewport.mainAxis is used. To reveal an object
properly in both axes, this method should be called for each Axis as the
returned RevealedOffset.offset only represents the offset of one of the
the two ScrollPositions.
See also:
- RevealedOffset, which describes the return value of this method.
Implementation
@override
RevealedOffset getOffsetToReveal(RenderObject target, double alignment, {Rect? rect, Axis? axis}) {
// WebF elements can scroll in both axes, but this method needs to work per axis
// Determine which axis to use based on the axis parameter or default to vertical
axis ??= Axis.vertical;
// Get the rect in target's coordinate space
rect ??= target.paintBounds;
// If target is not our descendant, return current scroll position
RenderObject? ancestor = target;
while (ancestor != null && ancestor != this) {
ancestor = ancestor.parent as RenderObject?;
}
if (ancestor == null) {
// Target is not our descendant, return current position
return RevealedOffset(
offset: axis == Axis.vertical ? scrollTop : scrollLeft,
rect: rect,
);
}
// Transform the rect from target's coordinate system to our coordinate system
final Matrix4 transform = target.getTransformTo(this);
Rect targetRect = MatrixUtils.transformRect(transform, rect);
// The targetRect is now in our viewport's coordinate system (already accounts for current scroll)
// To calculate the scroll offset needed, we need to work with the absolute position
// Add the current scroll offset to get the position in content space
final Rect targetInContentSpace = targetRect.translate(scrollLeft, scrollTop);
// Get our viewport size (visible area)
final Size viewportSize = scrollableViewportSize;
// Calculate the target offset based on alignment and axis
double targetOffset;
if (axis == Axis.vertical) {
// Vertical scrolling
final double targetTop = targetInContentSpace.top;
final double targetBottom = targetInContentSpace.bottom;
final double targetHeight = targetInContentSpace.height;
final double viewportHeight = viewportSize.height;
// Calculate position based on alignment (0.0 = top, 0.5 = center, 1.0 = bottom)
// For alignment=1.0 (bottom), we want the target at the bottom of the viewport
if (targetHeight >= viewportHeight) {
// Target is larger than viewport
if (alignment == 1.0) {
// Show the bottom of the target
targetOffset = targetBottom - viewportHeight;
} else if (alignment == 0.0) {
// Show the top of the target
targetOffset = targetTop;
} else {
// Center as much as possible
targetOffset = targetTop + (targetHeight - viewportHeight) * alignment;
}
} else {
// Target fits within viewport
if (alignment == 1.0) {
// Position target at bottom of viewport
targetOffset = targetBottom - viewportHeight;
} else if (alignment == 0.0) {
// Position target at top of viewport
targetOffset = targetTop;
} else {
// Position based on alignment
final double availableSpace = viewportHeight - targetHeight;
targetOffset = targetTop - (availableSpace * alignment);
}
}
// Clamp to valid scroll range
final double maxScroll = math.max(0.0, scrollableSize.height - viewportHeight);
targetOffset = targetOffset.clamp(0.0, maxScroll);
} else {
// Horizontal scrolling
final double targetLeft = targetInContentSpace.left;
final double targetRight = targetInContentSpace.right;
final double targetWidth = targetInContentSpace.width;
final double viewportWidth = viewportSize.width;
if (alignment == 1.0) {
// Align right of target with right of viewport
targetOffset = targetRight - viewportWidth;
} else if (alignment == 0.0) {
// Align left of target with left of viewport
targetOffset = targetLeft;
} else {
// General alignment
final double alignmentOffset = (viewportWidth - targetWidth) * alignment;
targetOffset = targetLeft - alignmentOffset;
}
// Clamp to valid scroll range
final double maxScroll = math.max(0.0, scrollableSize.width - viewportWidth);
targetOffset = targetOffset.clamp(0.0, maxScroll);
}
// Calculate the rect position in viewport space after scrolling
final Rect revealedRect;
if (axis == Axis.vertical) {
// After scrolling to targetOffset, the element will appear at:
// its content space position minus the new scroll offset
revealedRect = targetInContentSpace.translate(0.0, -targetOffset);
} else {
revealedRect = targetInContentSpace.translate(-targetOffset, 0.0);
}
return RevealedOffset(
offset: targetOffset,
rect: revealedRect,
);
}