Reject unaccepted pointers in Drag recognizer (#75943)

diff --git a/packages/flutter/lib/src/gestures/monodrag.dart b/packages/flutter/lib/src/gestures/monodrag.dart
index 82ab77d..15eaf30 100644
--- a/packages/flutter/lib/src/gestures/monodrag.dart
+++ b/packages/flutter/lib/src/gestures/monodrag.dart
@@ -309,15 +309,16 @@
       }
     }
     if (event is PointerUpEvent || event is PointerCancelEvent) {
-      _giveUpPointer(
-        event.pointer,
-        reject: event is PointerCancelEvent || _state ==_DragState.possible,
-      );
+      _giveUpPointer(event.pointer);
     }
   }
 
+  final Set<int> _acceptedActivePointers = <int>{};
+
   @override
   void acceptGesture(int pointer) {
+    assert(!_acceptedActivePointers.contains(pointer));
+    _acceptedActivePointers.add(pointer);
     if (_state != _DragState.accepted) {
       _state = _DragState.accepted;
       final OffsetPair delta = _pendingDragOffset;
@@ -384,32 +385,36 @@
     _state = _DragState.ready;
   }
 
-  void _giveUpPointer(int pointer, {bool reject = true}) {
+  void _giveUpPointer(int pointer) {
     stopTrackingPointer(pointer);
-    if (reject)
+    // If we never accepted the pointer, we reject it since we are no longer
+    // interested in winning the gesture arena for it.
+    if (!_acceptedActivePointers.remove(pointer))
       resolvePointer(pointer, GestureDisposition.rejected);
   }
 
   void _checkDown() {
     assert(_initialButtons == kPrimaryButton);
-    final DragDownDetails details = DragDownDetails(
-      globalPosition: _initialPosition.global,
-      localPosition: _initialPosition.local,
-    );
-    if (onDown != null)
+    if (onDown != null) {
+      final DragDownDetails details = DragDownDetails(
+        globalPosition: _initialPosition.global,
+        localPosition: _initialPosition.local,
+      );
       invokeCallback<void>('onDown', () => onDown!(details));
+    }
   }
 
   void _checkStart(Duration timestamp, int pointer) {
     assert(_initialButtons == kPrimaryButton);
-    final DragStartDetails details = DragStartDetails(
-      sourceTimeStamp: timestamp,
-      globalPosition: _initialPosition.global,
-      localPosition: _initialPosition.local,
-      kind: getKindForPointer(pointer),
-    );
-    if (onStart != null)
+    if (onStart != null) {
+      final DragStartDetails details = DragStartDetails(
+        sourceTimeStamp: timestamp,
+        globalPosition: _initialPosition.global,
+        localPosition: _initialPosition.local,
+        kind: getKindForPointer(pointer),
+      );
       invokeCallback<void>('onStart', () => onStart!(details));
+    }
   }
 
   void _checkUpdate({
@@ -420,15 +425,16 @@
     Offset? localPosition,
   }) {
     assert(_initialButtons == kPrimaryButton);
-    final DragUpdateDetails details = DragUpdateDetails(
-      sourceTimeStamp: sourceTimeStamp,
-      delta: delta,
-      primaryDelta: primaryDelta,
-      globalPosition: globalPosition,
-      localPosition: localPosition,
-    );
-    if (onUpdate != null)
+    if (onUpdate != null) {
+      final DragUpdateDetails details = DragUpdateDetails(
+        sourceTimeStamp: sourceTimeStamp,
+        delta: delta,
+        primaryDelta: primaryDelta,
+        globalPosition: globalPosition,
+        localPosition: localPosition,
+      );
       invokeCallback<void>('onUpdate', () => onUpdate!(details));
+    }
   }
 
   void _checkEnd(int pointer) {
diff --git a/packages/flutter/test/gestures/monodrag_test.dart b/packages/flutter/test/gestures/monodrag_test.dart
new file mode 100644
index 0000000..3ca3b07
--- /dev/null
+++ b/packages/flutter/test/gestures/monodrag_test.dart
@@ -0,0 +1,56 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/gestures.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import 'gesture_tester.dart';
+
+void main() {
+  setUp(ensureGestureBinding);
+
+  testGesture('do not crash on up event for a pending pointer after winning arena for another pointer', (GestureTester tester) {
+    // Regression test for https://github.com/flutter/flutter/issues/75061.
+
+    final VerticalDragGestureRecognizer v = VerticalDragGestureRecognizer()
+      ..onStart = (_) { };
+    final HorizontalDragGestureRecognizer h = HorizontalDragGestureRecognizer()
+      ..onStart = (_) { };
+
+    const PointerDownEvent down90 = PointerDownEvent(
+      pointer: 90,
+      position: Offset(10.0, 10.0),
+    );
+
+    const PointerUpEvent up90 = PointerUpEvent(
+      pointer: 90,
+      position: Offset(10.0, 10.0),
+    );
+
+    const PointerDownEvent down91 = PointerDownEvent(
+      pointer: 91,
+      position: Offset(20.0, 20.0),
+    );
+
+    const PointerUpEvent up91 = PointerUpEvent(
+      pointer: 91,
+      position: Offset(20.0, 20.0),
+    );
+
+    v.addPointer(down90);
+    GestureBinding.instance!.gestureArena.close(90);
+    h.addPointer(down91);
+    v.addPointer(down91);
+    GestureBinding.instance!.gestureArena.close(91);
+    tester.async.flushMicrotasks();
+
+    GestureBinding.instance!.handleEvent(up90, HitTestEntry(MockHitTestTarget()));
+    GestureBinding.instance!.handleEvent(up91, HitTestEntry(MockHitTestTarget()));
+  });
+}
+
+class MockHitTestTarget implements HitTestTarget {
+  @override
+  void handleEvent(PointerEvent event, HitTestEntry entry) { }
+}