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