Skip to content

Commit 250d8ae

Browse files
committed
scroll: Apply a min velocity in ScrollToEndSimulation
This makes it possible to see in a self-contained way, in this class's own code, that it always starts moving at a velocity that isn't zero, or less than zero, or at risk of being conflated with zero. This doesn't have a big effect in practice, because the only call site already does something else whenever the distance to travel is negative or very close to zero. But there is a small range -- namely where the distance to travel is between 1 and 12 physical pixels, given the default behavior of ScrollPhysics.toleranceFor -- in which this minimum speed does apply.
1 parent ac21040 commit 250d8ae

File tree

2 files changed

+29
-2
lines changed

2 files changed

+29
-2
lines changed

lib/widgets/scrolling.dart

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -364,12 +364,18 @@ class InertialSimulation extends Simulation { // TODO(upstream)
364364
/// Instead this takes a smoothed-out approximation of such a trajectory.
365365
class ScrollToEndSimulation extends InertialSimulation {
366366
factory ScrollToEndSimulation(ScrollPosition position) {
367+
final tolerance = position.physics.toleranceFor(position);
367368
final startPosition = position.pixels;
368369
final estimatedEndPosition = position.maxScrollExtent;
369370
final velocityForMinDuration = (estimatedEndPosition - startPosition)
370371
/ (minDuration.inMilliseconds / 1000.0);
371-
assert(velocityForMinDuration > 0);
372-
final velocity = clampDouble(velocityForMinDuration, 0, topSpeed);
372+
final velocity = clampDouble(velocityForMinDuration,
373+
// If the starting position is beyond the estimated end
374+
// (i.e. `velocityForMinDuration < 0`), or very close to it,
375+
// then move forward at a small positive velocity.
376+
// Let the overscroll handling bring the position to exactly the end.
377+
2 * tolerance.velocity,
378+
topSpeed);
373379
return ScrollToEndSimulation._(startPosition, velocity);
374380
}
375381

test/widgets/scrolling_test.dart

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,27 @@ void main() {
418418
debugDefaultTargetPlatformOverride = null;
419419
});
420420

421+
testWidgets('starting very near end, apply min speed', (tester) async {
422+
await prepare(tester, topHeight: 400, bottomHeight: 400);
423+
// Verify the assumption used for constructing the example numbers below.
424+
check(position.physics.toleranceFor(position).velocity)
425+
.isCloseTo(20/3, .01);
426+
427+
position.jumpTo(398);
428+
await tester.pump();
429+
check(position.extentAfter).equals(2);
430+
431+
position.scrollToEnd();
432+
await tester.pump();
433+
check(position.extentAfter).equals(2);
434+
435+
// Reach the end in just 150ms, not 300ms.
436+
await tester.pump(Duration(milliseconds: 75));
437+
check(position.extentAfter).equals(1);
438+
await tester.pump(Duration(milliseconds: 75));
439+
check(position.extentAfter).equals(0);
440+
});
441+
421442
testWidgets('on overscroll, stop', (tester) async {
422443
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
423444
await prepare(tester, topHeight: 400, bottomHeight: 1000);

0 commit comments

Comments
 (0)