applyBoundaryConditions method
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;
}