Skip to content

Commit

Permalink
Fix scroll jump when NestedScrollPosition is inertia-cancelled. (#116…
Browse files Browse the repository at this point in the history
…689)

* Fix scroll jump when NestedScrollPosition is inertia-cancelled.

* Switch to using pointerScroll(0)
  • Loading branch information
moffatman committed Dec 13, 2022
1 parent 6432fd1 commit 97df2b3
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 3 deletions.
5 changes: 4 additions & 1 deletion packages/flutter/lib/src/widgets/nested_scroll_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -903,7 +903,10 @@ class _NestedScrollCoordinator implements ScrollActivityDelegate, ScrollHoldCont
// If an update is made to pointer scrolling here, consider if the same
// (or similar) change should be made in
// ScrollPositionWithSingleContext.pointerScroll.
assert(delta != 0.0);
if (delta == 0.0) {
goBallistic(0.0);
return;
}

goIdle();
updateUserScrollDirection(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,10 @@ class ScrollPositionWithSingleContext extends ScrollPosition implements ScrollAc
// If an update is made to pointer scrolling here, consider if the same
// (or similar) change should be made in
// _NestedScrollCoordinator.pointerScroll.
assert(delta != 0.0);
if (delta == 0.0) {
goBallistic(0.0);
return;
}

final double targetPixels =
math.min(math.max(pixels + delta, minScrollExtent), maxScrollExtent);
Expand Down
2 changes: 1 addition & 1 deletion packages/flutter/lib/src/widgets/scrollable.dart
Original file line number Diff line number Diff line change
Expand Up @@ -780,7 +780,7 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin, R
GestureBinding.instance.pointerSignalResolver.register(event, _handlePointerScroll);
}
} else if (event is PointerScrollInertiaCancelEvent) {
position.jumpTo(position.pixels);
position.pointerScroll(0);
// Don't use the pointer signal resolver, all hit-tested scrollables should stop.
}
}
Expand Down
48 changes: 48 additions & 0 deletions packages/flutter/test/widgets/nested_scroll_view_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1012,6 +1012,54 @@ void main() {
);
});

testWidgets('Inertia-cancel event does not modify either position.', (WidgetTester tester) async {
final GlobalKey<NestedScrollViewState> globalKey = GlobalKey();
await tester.pumpWidget(buildTest(
key: globalKey,
expanded: false,
));

double appBarHeight = tester.renderObject<RenderBox>(find.byType(AppBar)).size.height;
expect(appBarHeight, 104.0);
final double scrollExtent = appBarHeight + 50.0;
expect(globalKey.currentState!.outerController.offset, 0.0);
expect(globalKey.currentState!.innerController.offset, 0.0);

// The scroll gesture should occur in the inner body, so the whole
// scroll view is scrolled.
final TestGesture gesture = await tester.startGesture(Offset(
0.0,
appBarHeight + 1.0,
));
await gesture.moveBy(Offset(0.0, -scrollExtent));
await tester.pump();

appBarHeight = tester.renderObject<RenderBox>(find.byType(AppBar)).size.height;
// This is not an expanded AppBar.
expect(appBarHeight, 104.0);
// The outer scroll controller should show an offset of the applied
// scrollExtent.
expect(globalKey.currentState!.outerController.offset, appBarHeight);
// the inner scroll controller should have scrolled equivalent to the
// difference between the applied scrollExtent and the outer extent.
expect(
globalKey.currentState!.innerController.offset,
scrollExtent - appBarHeight,
);

final TestPointer testPointer = TestPointer(3, ui.PointerDeviceKind.trackpad);
await tester.sendEventToBinding(testPointer.addPointer(
location: Offset(0.0, appBarHeight + 1.0)
));
await tester.sendEventToBinding(testPointer.scrollInertiaCancel());
// ensure no change.
expect(globalKey.currentState!.outerController.offset, appBarHeight);
expect(
globalKey.currentState!.innerController.offset,
scrollExtent - appBarHeight,
);
});

testWidgets('scrolling by less than the expanded outer extent does not scroll the inner body', (WidgetTester tester) async {
final GlobalKey<NestedScrollViewState> globalKey = GlobalKey();
await tester.pumpWidget(buildTest(key: globalKey));
Expand Down

0 comments on commit 97df2b3

Please sign in to comment.