diff --git a/packages/flutter/lib/src/gestures/monodrag.dart b/packages/flutter/lib/src/gestures/monodrag.dart index 5edc715e64..82ab77ddb9 100644 --- a/packages/flutter/lib/src/gestures/monodrag.dart +++ b/packages/flutter/lib/src/gestures/monodrag.dart @@ -386,12 +386,8 @@ abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer { void _giveUpPointer(int pointer, {bool reject = true}) { stopTrackingPointer(pointer); - if (reject) { - if (_velocityTrackers.containsKey(pointer)) { - _velocityTrackers.remove(pointer); - resolvePointer(pointer, GestureDisposition.rejected); - } - } + if (reject) + resolvePointer(pointer, GestureDisposition.rejected); } void _checkDown() { diff --git a/packages/flutter/lib/src/gestures/recognizer.dart b/packages/flutter/lib/src/gestures/recognizer.dart index 7eb0b56f1a..6231f792b7 100644 --- a/packages/flutter/lib/src/gestures/recognizer.dart +++ b/packages/flutter/lib/src/gestures/recognizer.dart @@ -268,8 +268,8 @@ abstract class OneSequenceGestureRecognizer extends GestureRecognizer { void resolvePointer(int pointer, GestureDisposition disposition) { final GestureArenaEntry? entry = _entries[pointer]; if (entry != null) { - entry.resolve(disposition); _entries.remove(pointer); + entry.resolve(disposition); } } diff --git a/packages/flutter/test/gestures/drag_test.dart b/packages/flutter/test/gestures/drag_test.dart index d7cc0f5642..725581bd2c 100644 --- a/packages/flutter/test/gestures/drag_test.dart +++ b/packages/flutter/test/gestures/drag_test.dart @@ -1151,4 +1151,75 @@ void main() { logs.clear(); }, ); + + testGesture( + 'On multiple pointers, the last tracking pointer can be rejected by [resolvePointer] when the ' + 'other pointer already accepted the VerticalDragGestureRecognizer', + (GestureTester tester) { + // Regressing test for https://github.com/flutter/flutter/issues/68373 + final List logs = []; + final VerticalDragGestureRecognizer drag = VerticalDragGestureRecognizer() + ..onDown = (DragDownDetails details) { logs.add('downD'); } + ..onStart = (DragStartDetails details) { logs.add('startD'); } + ..onUpdate = (DragUpdateDetails details) { logs.add('updateD'); } + ..onEnd = (DragEndDetails details) { logs.add('endD'); } + ..onCancel = () { logs.add('cancelD'); }; + // Competitor + final TapGestureRecognizer tap = TapGestureRecognizer() + ..onTapDown = (TapDownDetails details) { logs.add('downT'); } + ..onTapUp = (TapUpDetails details) { logs.add('upT'); } + ..onTapCancel = () {}; + addTearDown(tap.dispose); + addTearDown(drag.dispose); + + final TestPointer pointer1 = TestPointer(1, PointerDeviceKind.touch); + final TestPointer pointer2 = TestPointer(2, PointerDeviceKind.touch); + final TestPointer pointer3 = TestPointer(3, PointerDeviceKind.touch); + final TestPointer pointer4 = TestPointer(4, PointerDeviceKind.touch); + + final PointerDownEvent down1 = pointer1.down(const Offset(10.0, 10.0)); + final PointerDownEvent down2 = pointer2.down(const Offset(11.0, 11.0)); + final PointerDownEvent down3 = pointer3.down(const Offset(12.0, 12.0)); + final PointerDownEvent down4 = pointer4.down(const Offset(13.0, 13.0)); + + tap.addPointer(down1); + drag.addPointer(down1); + tester.closeArena(pointer1.pointer); + tester.route(down1); + expect(logs, ['downD']); + logs.clear(); + + tap.addPointer(down2); + drag.addPointer(down2); + tester.closeArena(pointer2.pointer); + tester.route(down2); + expect(logs, []); + + tap.addPointer(down3); + drag.addPointer(down3); + tester.closeArena(pointer3.pointer); + tester.route(down3); + expect(logs, []); + + drag.addPointer(down4); + tester.closeArena(pointer4.pointer); + tester.route(down4); + expect(logs, ['startD']); + logs.clear(); + + tester.route(pointer2.up()); + GestureBinding.instance!.gestureArena.sweep(pointer2.pointer); + expect(logs, []); + + tester.route(pointer4.cancel()); + expect(logs, []); + + tester.route(pointer3.cancel()); + expect(logs, []); + + tester.route(pointer1.cancel()); + expect(logs, ['endD']); + logs.clear(); + }, + ); }