fix an assertion causes by zero offset pointer scroll (#73016)
diff --git a/packages/flutter/lib/src/widgets/scrollable.dart b/packages/flutter/lib/src/widgets/scrollable.dart
index 50a0106..752b54b 100644
--- a/packages/flutter/lib/src/widgets/scrollable.dart
+++ b/packages/flutter/lib/src/widgets/scrollable.dart
@@ -629,8 +629,7 @@
// Returns the offset that should result from applying [event] to the current
// position, taking min/max scroll extent into account.
- double _targetScrollOffsetForPointerScroll(PointerScrollEvent event) {
- final double delta = _pointerSignalEventDelta(event);
+ double _targetScrollOffsetForPointerScroll(double delta) {
return math.min(math.max(position.pixels + delta, position.minScrollExtent),
position.maxScrollExtent);
}
@@ -653,9 +652,10 @@
if (_physics != null && !_physics!.shouldAcceptUserOffset(position)) {
return;
}
- final double targetScrollOffset = _targetScrollOffsetForPointerScroll(event);
+ final double delta = _pointerSignalEventDelta(event);
+ final double targetScrollOffset = _targetScrollOffsetForPointerScroll(delta);
// Only express interest in the event if it would actually result in a scroll.
- if (targetScrollOffset != position.pixels) {
+ if (delta != 0.0 && targetScrollOffset != position.pixels) {
GestureBinding.instance!.pointerSignalResolver.register(event, _handlePointerScroll);
}
}
@@ -663,9 +663,10 @@
void _handlePointerScroll(PointerEvent event) {
assert(event is PointerScrollEvent);
- final double targetScrollOffset = _targetScrollOffsetForPointerScroll(event as PointerScrollEvent);
- if (targetScrollOffset != position.pixels) {
- position.pointerScroll(_pointerSignalEventDelta(event));
+ final double delta = _pointerSignalEventDelta(event as PointerScrollEvent);
+ final double targetScrollOffset = _targetScrollOffsetForPointerScroll(delta);
+ if (delta != 0.0 && targetScrollOffset != position.pixels) {
+ position.pointerScroll(delta);
}
}
diff --git a/packages/flutter/test/widgets/scrollable_test.dart b/packages/flutter/test/widgets/scrollable_test.dart
index 426f122..432d6d2 100644
--- a/packages/flutter/test/widgets/scrollable_test.dart
+++ b/packages/flutter/test/widgets/scrollable_test.dart
@@ -1206,6 +1206,47 @@
expect(outerController.position.pixels, 0.0);
expect(innerController.position.pixels, 0.0);
});
+
+ // Regression test for https://github.com/flutter/flutter/issues/71949
+ testWidgets('Zero offset pointer scroll should not trigger an assertion.', (WidgetTester tester) async {
+ final ScrollController controller = ScrollController();
+ Widget build(double height) {
+ return MaterialApp(
+ home: Scaffold(
+ body: SizedBox(
+ width: double.infinity,
+ height: height,
+ child: SingleChildScrollView(
+ controller: controller,
+ child: const SizedBox(
+ width: double.infinity,
+ height: 300.0,
+ ),
+ ),
+ )
+ ),
+ );
+ }
+
+ await tester.pumpWidget(build(200.0));
+ expect(controller.position.pixels, 0.0);
+
+ controller.jumpTo(100.0);
+ expect(controller.position.pixels, 100.0);
+
+ // Make the outer constraints larger that the scrollable widget is no longer able to scroll.
+ await tester.pumpWidget(build(300.0));
+ expect(controller.position.pixels, 100.0);
+ expect(controller.position.maxScrollExtent, 0.0);
+
+ // Hover over the scroll view and create a zero offset pointer scroll.
+ final Offset scrollable = tester.getCenter(find.byType(SingleChildScrollView));
+ final TestPointer testPointer = TestPointer(1, ui.PointerDeviceKind.mouse);
+ testPointer.hover(scrollable);
+ await tester.sendEventToBinding(testPointer.scroll(const Offset(0.0, 0.0)));
+
+ expect(tester.takeException(), null);
+ });
}
// ignore: must_be_immutable