applyBoundaryConditions method

  1. @override
double applyBoundaryConditions(
  1. ScrollMetrics position,
  2. double value
)
override

Determines the overscroll by applying the boundary conditions.

Called by ScrollPosition.applyBoundaryConditions, which is called by ScrollPosition.setPixels just before the ScrollPosition.pixels value is updated, to determine how much of the offset is to be clamped off and sent to ScrollPosition.didOverscrollBy.

The value argument is guaranteed to not equal the ScrollMetrics.pixels of the position argument when this is called.

It is possible for this method to be called when the position describes an already-out-of-bounds position. In that case, the boundary conditions should usually only prevent a further increase in the extent to which the position is out of bounds, allowing a decrease to be applied successfully, so that (for instance) an animation can smoothly snap an out of bounds position to the bounds. See BallisticScrollActivity.

This method must not clamp parts of the offset that are entirely within the bounds described by the given position.

The given position is only valid during this method call. Do not keep a reference to it to use later, as the values may update, may not update, or may update to reflect an entirely unrelated scrollable.

Examples

BouncingScrollPhysics returns zero. In other words, it allows scrolling past the boundary unhindered.

ClampingScrollPhysics returns the amount by which the value is beyond the position or the boundary, whichever is furthest from the content. In other words, it disallows scrolling past the boundary, but allows scrolling back from being overscrolled, if for some reason the position ends up overscrolled.

Implementation

@override
double applyBoundaryConditions(ScrollMetrics position, double value) {
  final ScrollPosition scrollPosition = position as ScrollPosition;
  viewportRender ??=
      findViewport(controller!.position?.context.storageContext);
  bool notFull = position.minScrollExtent == position.maxScrollExtent;
  final bool enablePullDown = viewportRender == null
      ? false
      : viewportRender!.firstChild is RenderSliverRefresh;
  final bool enablePullUp = viewportRender == null
      ? false
      : viewportRender!.lastChild is RenderSliverLoading;
  if (controller!.headerMode!.value == RefreshStatus.twoLeveling) {
    if (position.pixels - value > 0.0) {
      return parent!.applyBoundaryConditions(position, value);
    }
  } else {
    if ((position.pixels - value > 0.0 && !enablePullDown) ||
        (position.pixels - value < 0 && !enablePullUp)) {
      return parent!.applyBoundaryConditions(position, value);
    }
  }
  double topExtra = 0.0;
  double? bottomExtra = 0.0;
  if (enablePullDown) {
    final RenderSliverRefresh sliverHeader =
        viewportRender!.firstChild as RenderSliverRefresh;
    topExtra = sliverHeader.hasLayoutExtent
        ? 0.0
        : sliverHeader.refreshIndicatorLayoutExtent;
  }
  if (enablePullUp) {
    final RenderSliverLoading? sliverFooter =
        viewportRender!.lastChild as RenderSliverLoading?;
    bottomExtra = (!notFull && sliverFooter!.geometry!.scrollExtent != 0) ||
            (notFull &&
                controller!.footerStatus == LoadStatus.noMore &&
                !RefreshConfiguration.of(
                        controller!.position!.context.storageContext)!
                    .enableLoadingWhenNoData) ||
            (notFull &&
                (RefreshConfiguration.of(
                            controller!.position!.context.storageContext)
                        ?.hideFooterWhenNotFull ??
                    false))
        ? 0.0
        : sliverFooter!.layoutExtent;
  }
  final double topBoundary =
      position.minScrollExtent - maxOverScrollExtent! - topExtra;
  final double bottomBoundary =
      position.maxScrollExtent + maxUnderScrollExtent! + bottomExtra!;

  if (scrollPosition.activity is BallisticScrollActivity) {
    if (topHitBoundary != double.infinity) {
      if (value < -topHitBoundary! && -topHitBoundary! <= position.pixels) {
        // hit top edge
        return value + topHitBoundary!;
      }
    }
    if (bottomHitBoundary != double.infinity) {
      if (position.pixels < bottomHitBoundary! + position.maxScrollExtent &&
          bottomHitBoundary! + position.maxScrollExtent < value) {
        // hit bottom edge
        return value - bottomHitBoundary! - position.maxScrollExtent;
      }
    }
  }
  if (maxOverScrollExtent != double.infinity &&
      value < topBoundary &&
      topBoundary < position.pixels) // hit top edge
    return value - topBoundary;
  if (maxUnderScrollExtent != double.infinity &&
      position.pixels < bottomBoundary &&
      bottomBoundary < value) {
    // hit bottom edge
    return value - bottomBoundary;
  }

  // check user is dragging,it is import,some devices may not bounce with different frame and time,bouncing return the different velocity
  if (scrollPosition.activity is DragScrollActivity) {
    if (maxOverScrollExtent != double.infinity &&
        value < position.pixels &&
        position.pixels <= topBoundary) // underscroll
      return value - position.pixels;
    if (maxUnderScrollExtent != double.infinity &&
        bottomBoundary <= position.pixels &&
        position.pixels < value) // overscroll
      return value - position.pixels;
  }
  return 0.0;
}