getOffsetToReveal method

  1. @override
RevealedOffset getOffsetToReveal (RenderObject target, double alignment, { Rect rect })
override

Returns the offset that would be needed to reveal the target RenderObject.

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.

See also:

Implementation

@override
RevealedOffset getOffsetToReveal(RenderObject target, double alignment, { Rect rect }) {
  double leadingScrollOffset = 0.0;
  double targetMainAxisExtent;
  rect ??= target.paintBounds;

  // Starting at `target` and walking towards the root:
  //  - `child` will be the last object before we reach this viewport, and
  //  - `pivot` will be the last RenderBox before we reach this viewport.
  RenderObject child = target;
  RenderBox pivot;
  bool onlySlivers = target is RenderSliver; // ... between viewport and `target` (`target` included).
  while (child.parent != this) {
    assert(child.parent != null, '$target must be a descendant of $this');
    if (child is RenderBox) {
      pivot = child;
    }
    if (child.parent is RenderSliver) {
      final RenderSliver parent = child.parent;
      leadingScrollOffset += parent.childScrollOffset(child);
    } else {
      onlySlivers = false;
      leadingScrollOffset = 0.0;
    }
    child = child.parent;
  }

  if (pivot != null) {
    assert(pivot.parent != null);
    assert(pivot.parent != this);
    assert(pivot != this);
    assert(pivot.parent is RenderSliver);  // TODO(abarth): Support other kinds of render objects besides slivers.
    final RenderSliver pivotParent = pivot.parent;

    final Matrix4 transform = target.getTransformTo(pivot);
    final Rect bounds = MatrixUtils.transformRect(transform, rect);

    final GrowthDirection growthDirection = pivotParent.constraints.growthDirection;
    switch (applyGrowthDirectionToAxisDirection(axisDirection, growthDirection)) {
      case AxisDirection.up:
        double offset;
        switch (growthDirection) {
          case GrowthDirection.forward:
            offset = bounds.bottom;
            break;
          case GrowthDirection.reverse:
            offset = bounds.top;
            break;
        }
        leadingScrollOffset += pivot.size.height - offset;
        targetMainAxisExtent = bounds.height;
        break;
      case AxisDirection.right:
        double offset;
        switch (growthDirection) {
          case GrowthDirection.forward:
            offset = bounds.left;
            break;
          case GrowthDirection.reverse:
            offset = bounds.right;
            break;
        }
        leadingScrollOffset += offset;
        targetMainAxisExtent = bounds.width;
        break;
      case AxisDirection.down:
        double offset;
        switch (growthDirection) {
          case GrowthDirection.forward:
            offset = bounds.top;
            break;
          case GrowthDirection.reverse:
            offset = bounds.bottom;
            break;
        }
        leadingScrollOffset += offset;
        targetMainAxisExtent = bounds.height;
        break;
      case AxisDirection.left:
        double offset;
        switch (growthDirection) {
          case GrowthDirection.forward:
            offset = bounds.right;
            break;
          case GrowthDirection.reverse:
            offset = bounds.left;
            break;
        }
        leadingScrollOffset += pivot.size.width - offset;
        targetMainAxisExtent = bounds.width;
        break;
    }
  } else if (onlySlivers) {
    final RenderSliver targetSliver = target;
    targetMainAxisExtent = targetSliver.geometry.scrollExtent;
  } else {
    return RevealedOffset(offset: offset.pixels, rect: rect);
  }

  assert(child.parent == this);
  assert(child is RenderSliver);
  final RenderSliver sliver = child;
  final double extentOfPinnedSlivers = maxScrollObstructionExtentBefore(sliver);
  leadingScrollOffset = scrollOffsetOf(sliver, leadingScrollOffset);
  switch (sliver.constraints.growthDirection) {
    case GrowthDirection.forward:
      leadingScrollOffset -= extentOfPinnedSlivers;
      break;
    case GrowthDirection.reverse:
      // Nothing to do.
      break;
  }

  double mainAxisExtent;
  switch (axis) {
    case Axis.horizontal:
      mainAxisExtent = size.width - extentOfPinnedSlivers;
      break;
    case Axis.vertical:
      mainAxisExtent = size.height - extentOfPinnedSlivers;
      break;
  }

  final double targetOffset = leadingScrollOffset - (mainAxisExtent - targetMainAxisExtent) * alignment;
  final double offsetDifference = offset.pixels - targetOffset;

  final Matrix4 transform = target.getTransformTo(this);
  applyPaintTransform(child, transform);
  Rect targetRect = MatrixUtils.transformRect(transform, rect);

  switch (axisDirection) {
    case AxisDirection.down:
      targetRect = targetRect.translate(0.0, offsetDifference);
      break;
    case AxisDirection.right:
      targetRect = targetRect.translate(offsetDifference, 0.0);
      break;
    case AxisDirection.up:
      targetRect = targetRect.translate(0.0, -offsetDifference);
      break;
    case AxisDirection.left:
      targetRect = targetRect.translate(-offsetDifference, 0.0);
      break;
  }

  return RevealedOffset(offset: targetOffset, rect: targetRect);
}