Unverified Commit bd938b00 authored by Renzo Olivares's avatar Renzo Olivares Committed by GitHub

Fix tap/drag callbacks firing when TapAndDragGestureRecognizer has not won the arena (#118342)

* Prevent drag and tap from accepting when a tap down exceeds the recognizers deadline but the recognizer has not won the arena

* Add test

* make analyzer happy
Co-authored-by: 's avatarRenzo Olivares <roliv@google.com>
parent 3e00520f
...@@ -850,6 +850,7 @@ class TapAndDragGestureRecognizer extends OneSequenceGestureRecognizer with _Tap ...@@ -850,6 +850,7 @@ class TapAndDragGestureRecognizer extends OneSequenceGestureRecognizer with _Tap
// Tap related state. // Tap related state.
bool _pastSlopTolerance = false; bool _pastSlopTolerance = false;
bool _sentTapDown = false; bool _sentTapDown = false;
bool _wonArenaForPrimaryPointer = false;
// Primary pointer being tracked by this recognizer. // Primary pointer being tracked by this recognizer.
int? _primaryPointer; int? _primaryPointer;
...@@ -934,7 +935,7 @@ class TapAndDragGestureRecognizer extends OneSequenceGestureRecognizer with _Tap ...@@ -934,7 +935,7 @@ class TapAndDragGestureRecognizer extends OneSequenceGestureRecognizer with _Tap
void handleNonAllowedPointer(PointerDownEvent event) { void handleNonAllowedPointer(PointerDownEvent event) {
// There can be multiple drags simultaneously. Their effects are combined. // There can be multiple drags simultaneously. Their effects are combined.
if (event.buttons != kPrimaryButton) { if (event.buttons != kPrimaryButton) {
if (!_sentTapDown) { if (!_wonArenaForPrimaryPointer) {
super.handleNonAllowedPointer(event); super.handleNonAllowedPointer(event);
} }
} }
...@@ -956,6 +957,8 @@ class TapAndDragGestureRecognizer extends OneSequenceGestureRecognizer with _Tap ...@@ -956,6 +957,8 @@ class TapAndDragGestureRecognizer extends OneSequenceGestureRecognizer with _Tap
_checkTapDown(currentDown!); _checkTapDown(currentDown!);
} }
_wonArenaForPrimaryPointer = true;
if (_start != null) { if (_start != null) {
_acceptDrag(_start!); _acceptDrag(_start!);
} }
...@@ -976,7 +979,7 @@ class TapAndDragGestureRecognizer extends OneSequenceGestureRecognizer with _Tap ...@@ -976,7 +979,7 @@ class TapAndDragGestureRecognizer extends OneSequenceGestureRecognizer with _Tap
case _DragState.possible: case _DragState.possible:
if (_pastSlopTolerance) { if (_pastSlopTolerance) {
// This means the pointer was not accepted as a tap. // This means the pointer was not accepted as a tap.
if (_sentTapDown) { if (_wonArenaForPrimaryPointer) {
// If the recognizer has already won the arena for the primary pointer being tracked // If the recognizer has already won the arena for the primary pointer being tracked
// but the pointer has exceeded the tap tolerance, then the pointer is accepted as a // but the pointer has exceeded the tap tolerance, then the pointer is accepted as a
// drag gesture. // drag gesture.
...@@ -1043,7 +1046,7 @@ class TapAndDragGestureRecognizer extends OneSequenceGestureRecognizer with _Tap ...@@ -1043,7 +1046,7 @@ class TapAndDragGestureRecognizer extends OneSequenceGestureRecognizer with _Tap
// This can occur when the recognizer is accepted before a [PointerMoveEvent] has been // This can occur when the recognizer is accepted before a [PointerMoveEvent] has been
// received that moves the pointer a sufficient global distance to be considered a drag. // received that moves the pointer a sufficient global distance to be considered a drag.
if (_start != null && _sentTapDown) { if (_start != null) {
_acceptDrag(_start!); _acceptDrag(_start!);
} }
} }
...@@ -1085,6 +1088,9 @@ class TapAndDragGestureRecognizer extends OneSequenceGestureRecognizer with _Tap ...@@ -1085,6 +1088,9 @@ class TapAndDragGestureRecognizer extends OneSequenceGestureRecognizer with _Tap
String get debugDescription => 'tap_and_drag'; String get debugDescription => 'tap_and_drag';
void _acceptDrag(PointerEvent event) { void _acceptDrag(PointerEvent event) {
if (!_wonArenaForPrimaryPointer) {
return;
}
_dragState = _DragState.accepted; _dragState = _DragState.accepted;
if (dragStartBehavior == DragStartBehavior.start) { if (dragStartBehavior == DragStartBehavior.start) {
_initialPosition = _initialPosition + OffsetPair(global: event.delta, local: event.localDelta); _initialPosition = _initialPosition + OffsetPair(global: event.delta, local: event.localDelta);
...@@ -1138,7 +1144,7 @@ class TapAndDragGestureRecognizer extends OneSequenceGestureRecognizer with _Tap ...@@ -1138,7 +1144,7 @@ class TapAndDragGestureRecognizer extends OneSequenceGestureRecognizer with _Tap
} }
void _checkTapUp(PointerUpEvent event) { void _checkTapUp(PointerUpEvent event) {
if (!_sentTapDown) { if (!_wonArenaForPrimaryPointer) {
return; return;
} }
...@@ -1265,6 +1271,7 @@ class TapAndDragGestureRecognizer extends OneSequenceGestureRecognizer with _Tap ...@@ -1265,6 +1271,7 @@ class TapAndDragGestureRecognizer extends OneSequenceGestureRecognizer with _Tap
void _resetTaps() { void _resetTaps() {
_sentTapDown = false; _sentTapDown = false;
_wonArenaForPrimaryPointer = false;
_primaryPointer = null; _primaryPointer = null;
} }
......
...@@ -12452,6 +12452,57 @@ void main() { ...@@ -12452,6 +12452,57 @@ void main() {
}); });
}); });
testWidgets('TextField does not leak touch events when deadline has exceeded', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/118340.
int textFieldTapCount = 0;
int prefixTapCount = 0;
int suffixTapCount = 0;
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: TextField(
onTap: () { textFieldTapCount += 1; },
decoration: InputDecoration(
labelText: 'Label',
prefix: ElevatedButton(
onPressed: () { prefixTapCount += 1; },
child: const Text('prefix'),
),
suffix: ElevatedButton(
onPressed: () { suffixTapCount += 1; },
child: const Text('suffix'),
),
),
),
),
),
);
TestGesture gesture =
await tester.startGesture(
tester.getRect(find.text('prefix')).center,
pointer: 7,
kind: PointerDeviceKind.mouse,
);
await tester.pumpAndSettle();
await gesture.up();
expect(textFieldTapCount, 0);
expect(prefixTapCount, 1);
expect(suffixTapCount, 0);
gesture = await tester.startGesture(
tester.getRect(find.text('suffix')).center,
pointer: 7,
kind: PointerDeviceKind.mouse,
);
await tester.pumpAndSettle();
await gesture.up();
expect(textFieldTapCount, 0);
expect(prefixTapCount, 1);
expect(suffixTapCount, 1);
});
testWidgets('prefix/suffix buttons do not leak touch events', (WidgetTester tester) async { testWidgets('prefix/suffix buttons do not leak touch events', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/39376. // Regression test for https://github.com/flutter/flutter/issues/39376.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment