Skip to content

Commit 6afbb25

Browse files
authored
Reject unaccepted pointers in Drag recognizer (flutter#75943)
1 parent 46b8ea8 commit 6afbb25

File tree

2 files changed

+88
-26
lines changed

2 files changed

+88
-26
lines changed

packages/flutter/lib/src/gestures/monodrag.dart

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -309,15 +309,16 @@ abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer {
309309
}
310310
}
311311
if (event is PointerUpEvent || event is PointerCancelEvent) {
312-
_giveUpPointer(
313-
event.pointer,
314-
reject: event is PointerCancelEvent || _state ==_DragState.possible,
315-
);
312+
_giveUpPointer(event.pointer);
316313
}
317314
}
318315

316+
final Set<int> _acceptedActivePointers = <int>{};
317+
319318
@override
320319
void acceptGesture(int pointer) {
320+
assert(!_acceptedActivePointers.contains(pointer));
321+
_acceptedActivePointers.add(pointer);
321322
if (_state != _DragState.accepted) {
322323
_state = _DragState.accepted;
323324
final OffsetPair delta = _pendingDragOffset;
@@ -384,32 +385,36 @@ abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer {
384385
_state = _DragState.ready;
385386
}
386387

387-
void _giveUpPointer(int pointer, {bool reject = true}) {
388+
void _giveUpPointer(int pointer) {
388389
stopTrackingPointer(pointer);
389-
if (reject)
390+
// If we never accepted the pointer, we reject it since we are no longer
391+
// interested in winning the gesture arena for it.
392+
if (!_acceptedActivePointers.remove(pointer))
390393
resolvePointer(pointer, GestureDisposition.rejected);
391394
}
392395

393396
void _checkDown() {
394397
assert(_initialButtons == kPrimaryButton);
395-
final DragDownDetails details = DragDownDetails(
396-
globalPosition: _initialPosition.global,
397-
localPosition: _initialPosition.local,
398-
);
399-
if (onDown != null)
398+
if (onDown != null) {
399+
final DragDownDetails details = DragDownDetails(
400+
globalPosition: _initialPosition.global,
401+
localPosition: _initialPosition.local,
402+
);
400403
invokeCallback<void>('onDown', () => onDown!(details));
404+
}
401405
}
402406

403407
void _checkStart(Duration timestamp, int pointer) {
404408
assert(_initialButtons == kPrimaryButton);
405-
final DragStartDetails details = DragStartDetails(
406-
sourceTimeStamp: timestamp,
407-
globalPosition: _initialPosition.global,
408-
localPosition: _initialPosition.local,
409-
kind: getKindForPointer(pointer),
410-
);
411-
if (onStart != null)
409+
if (onStart != null) {
410+
final DragStartDetails details = DragStartDetails(
411+
sourceTimeStamp: timestamp,
412+
globalPosition: _initialPosition.global,
413+
localPosition: _initialPosition.local,
414+
kind: getKindForPointer(pointer),
415+
);
412416
invokeCallback<void>('onStart', () => onStart!(details));
417+
}
413418
}
414419

415420
void _checkUpdate({
@@ -420,15 +425,16 @@ abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer {
420425
Offset? localPosition,
421426
}) {
422427
assert(_initialButtons == kPrimaryButton);
423-
final DragUpdateDetails details = DragUpdateDetails(
424-
sourceTimeStamp: sourceTimeStamp,
425-
delta: delta,
426-
primaryDelta: primaryDelta,
427-
globalPosition: globalPosition,
428-
localPosition: localPosition,
429-
);
430-
if (onUpdate != null)
428+
if (onUpdate != null) {
429+
final DragUpdateDetails details = DragUpdateDetails(
430+
sourceTimeStamp: sourceTimeStamp,
431+
delta: delta,
432+
primaryDelta: primaryDelta,
433+
globalPosition: globalPosition,
434+
localPosition: localPosition,
435+
);
431436
invokeCallback<void>('onUpdate', () => onUpdate!(details));
437+
}
432438
}
433439

434440
void _checkEnd(int pointer) {
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'package:flutter/gestures.dart';
6+
import 'package:flutter_test/flutter_test.dart';
7+
8+
import 'gesture_tester.dart';
9+
10+
void main() {
11+
setUp(ensureGestureBinding);
12+
13+
testGesture('do not crash on up event for a pending pointer after winning arena for another pointer', (GestureTester tester) {
14+
// Regression test for https://github.com/flutter/flutter/issues/75061.
15+
16+
final VerticalDragGestureRecognizer v = VerticalDragGestureRecognizer()
17+
..onStart = (_) { };
18+
final HorizontalDragGestureRecognizer h = HorizontalDragGestureRecognizer()
19+
..onStart = (_) { };
20+
21+
const PointerDownEvent down90 = PointerDownEvent(
22+
pointer: 90,
23+
position: Offset(10.0, 10.0),
24+
);
25+
26+
const PointerUpEvent up90 = PointerUpEvent(
27+
pointer: 90,
28+
position: Offset(10.0, 10.0),
29+
);
30+
31+
const PointerDownEvent down91 = PointerDownEvent(
32+
pointer: 91,
33+
position: Offset(20.0, 20.0),
34+
);
35+
36+
const PointerUpEvent up91 = PointerUpEvent(
37+
pointer: 91,
38+
position: Offset(20.0, 20.0),
39+
);
40+
41+
v.addPointer(down90);
42+
GestureBinding.instance!.gestureArena.close(90);
43+
h.addPointer(down91);
44+
v.addPointer(down91);
45+
GestureBinding.instance!.gestureArena.close(91);
46+
tester.async.flushMicrotasks();
47+
48+
GestureBinding.instance!.handleEvent(up90, HitTestEntry(MockHitTestTarget()));
49+
GestureBinding.instance!.handleEvent(up91, HitTestEntry(MockHitTestTarget()));
50+
});
51+
}
52+
53+
class MockHitTestTarget implements HitTestTarget {
54+
@override
55+
void handleEvent(PointerEvent event, HitTestEntry entry) { }
56+
}

0 commit comments

Comments
 (0)