Skip to content

Commit 97df2b3

Browse files
authored
Fix scroll jump when NestedScrollPosition is inertia-cancelled. (#116689)
* Fix scroll jump when NestedScrollPosition is inertia-cancelled. * Switch to using pointerScroll(0)
1 parent 6432fd1 commit 97df2b3

File tree

4 files changed

+57
-3
lines changed

4 files changed

+57
-3
lines changed

packages/flutter/lib/src/widgets/nested_scroll_view.dart

+4-1
Original file line numberDiff line numberDiff line change
@@ -903,7 +903,10 @@ class _NestedScrollCoordinator implements ScrollActivityDelegate, ScrollHoldCont
903903
// If an update is made to pointer scrolling here, consider if the same
904904
// (or similar) change should be made in
905905
// ScrollPositionWithSingleContext.pointerScroll.
906-
assert(delta != 0.0);
906+
if (delta == 0.0) {
907+
goBallistic(0.0);
908+
return;
909+
}
907910

908911
goIdle();
909912
updateUserScrollDirection(

packages/flutter/lib/src/widgets/scroll_position_with_single_context.dart

+4-1
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,10 @@ class ScrollPositionWithSingleContext extends ScrollPosition implements ScrollAc
212212
// If an update is made to pointer scrolling here, consider if the same
213213
// (or similar) change should be made in
214214
// _NestedScrollCoordinator.pointerScroll.
215-
assert(delta != 0.0);
215+
if (delta == 0.0) {
216+
goBallistic(0.0);
217+
return;
218+
}
216219

217220
final double targetPixels =
218221
math.min(math.max(pixels + delta, minScrollExtent), maxScrollExtent);

packages/flutter/lib/src/widgets/scrollable.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -780,7 +780,7 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin, R
780780
GestureBinding.instance.pointerSignalResolver.register(event, _handlePointerScroll);
781781
}
782782
} else if (event is PointerScrollInertiaCancelEvent) {
783-
position.jumpTo(position.pixels);
783+
position.pointerScroll(0);
784784
// Don't use the pointer signal resolver, all hit-tested scrollables should stop.
785785
}
786786
}

packages/flutter/test/widgets/nested_scroll_view_test.dart

+48
Original file line numberDiff line numberDiff line change
@@ -1012,6 +1012,54 @@ void main() {
10121012
);
10131013
});
10141014

1015+
testWidgets('Inertia-cancel event does not modify either position.', (WidgetTester tester) async {
1016+
final GlobalKey<NestedScrollViewState> globalKey = GlobalKey();
1017+
await tester.pumpWidget(buildTest(
1018+
key: globalKey,
1019+
expanded: false,
1020+
));
1021+
1022+
double appBarHeight = tester.renderObject<RenderBox>(find.byType(AppBar)).size.height;
1023+
expect(appBarHeight, 104.0);
1024+
final double scrollExtent = appBarHeight + 50.0;
1025+
expect(globalKey.currentState!.outerController.offset, 0.0);
1026+
expect(globalKey.currentState!.innerController.offset, 0.0);
1027+
1028+
// The scroll gesture should occur in the inner body, so the whole
1029+
// scroll view is scrolled.
1030+
final TestGesture gesture = await tester.startGesture(Offset(
1031+
0.0,
1032+
appBarHeight + 1.0,
1033+
));
1034+
await gesture.moveBy(Offset(0.0, -scrollExtent));
1035+
await tester.pump();
1036+
1037+
appBarHeight = tester.renderObject<RenderBox>(find.byType(AppBar)).size.height;
1038+
// This is not an expanded AppBar.
1039+
expect(appBarHeight, 104.0);
1040+
// The outer scroll controller should show an offset of the applied
1041+
// scrollExtent.
1042+
expect(globalKey.currentState!.outerController.offset, appBarHeight);
1043+
// the inner scroll controller should have scrolled equivalent to the
1044+
// difference between the applied scrollExtent and the outer extent.
1045+
expect(
1046+
globalKey.currentState!.innerController.offset,
1047+
scrollExtent - appBarHeight,
1048+
);
1049+
1050+
final TestPointer testPointer = TestPointer(3, ui.PointerDeviceKind.trackpad);
1051+
await tester.sendEventToBinding(testPointer.addPointer(
1052+
location: Offset(0.0, appBarHeight + 1.0)
1053+
));
1054+
await tester.sendEventToBinding(testPointer.scrollInertiaCancel());
1055+
// ensure no change.
1056+
expect(globalKey.currentState!.outerController.offset, appBarHeight);
1057+
expect(
1058+
globalKey.currentState!.innerController.offset,
1059+
scrollExtent - appBarHeight,
1060+
);
1061+
});
1062+
10151063
testWidgets('scrolling by less than the expanded outer extent does not scroll the inner body', (WidgetTester tester) async {
10161064
final GlobalKey<NestedScrollViewState> globalKey = GlobalKey();
10171065
await tester.pumpWidget(buildTest(key: globalKey));

0 commit comments

Comments
 (0)