showInViewport method Null safety

Rect? showInViewport(
  1. {RenderObject? descendant,
  2. Rect? rect,
  3. required RenderAbstractViewport viewport,
  4. required ViewportOffset offset,
  5. Duration duration = Duration.zero,
  6. Curve curve = Curves.ease}
)

Make (a portion of) the given descendant of the given viewport fully visible in the viewport by manipulating the provided ViewportOffset offset.

The optional rect parameter describes which area of the descendant should be shown in the viewport. If rect is null, the entire descendant will be revealed. The rect parameter is interpreted relative to the coordinate system of descendant.

The returned Rect describes the new location of descendant or rect in the viewport after it has been revealed. See RevealedOffset.rect for a full definition of this Rect.

The parameters viewport and offset are required and cannot be null. If descendant is null, this is a no-op and rect is returned.

If both descendant and rect are null, null is returned because there is nothing to be shown in the viewport.

The duration parameter can be set to a non-zero value to animate the target object into the viewport with an animation defined by curve.

See also:

Implementation

static Rect? showInViewport({
  RenderObject? descendant,
  Rect? rect,
  required RenderAbstractViewport viewport,
  required ViewportOffset offset,
  Duration duration = Duration.zero,
  Curve curve = Curves.ease,
}) {
  assert(viewport != null);
  assert(offset != null);
  if (descendant == null) {
    return rect;
  }
  final RevealedOffset leadingEdgeOffset = viewport.getOffsetToReveal(descendant, 0.0, rect: rect);
  final RevealedOffset trailingEdgeOffset = viewport.getOffsetToReveal(descendant, 1.0, rect: rect);
  final double currentOffset = offset.pixels;

  //           scrollOffset
  //                       0 +---------+
  //                         |         |
  //                       _ |         |
  //    viewport position |  |         |
  // with `descendant` at |  |         | _
  //        trailing edge |_ | xxxxxxx |  | viewport position
  //                         |         |  | with `descendant` at
  //                         |         | _| leading edge
  //                         |         |
  //                     800 +---------+
  //
  // `trailingEdgeOffset`: Distance from scrollOffset 0 to the start of the
  //                       viewport on the left in image above.
  // `leadingEdgeOffset`: Distance from scrollOffset 0 to the start of the
  //                      viewport on the right in image above.
  //
  // The viewport position on the left is achieved by setting `offset.pixels`
  // to `trailingEdgeOffset`, the one on the right by setting it to
  // `leadingEdgeOffset`.

  final RevealedOffset targetOffset;
  if (leadingEdgeOffset.offset < trailingEdgeOffset.offset) {
    // `descendant` is too big to be visible on screen in its entirety. Let's
    // align it with the edge that requires the least amount of scrolling.
    final double leadingEdgeDiff = (offset.pixels - leadingEdgeOffset.offset).abs();
    final double trailingEdgeDiff = (offset.pixels - trailingEdgeOffset.offset).abs();
    targetOffset = leadingEdgeDiff < trailingEdgeDiff ? leadingEdgeOffset : trailingEdgeOffset;
  } else if (currentOffset > leadingEdgeOffset.offset) {
    // `descendant` currently starts above the leading edge and can be shown
    // fully on screen by scrolling down (which means: moving viewport up).
    targetOffset = leadingEdgeOffset;
  } else if (currentOffset < trailingEdgeOffset.offset) {
    // `descendant currently ends below the trailing edge and can be shown
    // fully on screen by scrolling up (which means: moving viewport down)
    targetOffset = trailingEdgeOffset;
  } else {
    // `descendant` is between leading and trailing edge and hence already
    //  fully shown on screen. No action necessary.
    final Matrix4 transform = descendant.getTransformTo(viewport.parent! as RenderObject);
    return MatrixUtils.transformRect(transform, rect ?? descendant.paintBounds);
  }

  assert(targetOffset != null);

  offset.moveTo(targetOffset.offset, duration: duration, curve: curve);
  return targetOffset.rect;
}