Skip to content

Commit bd938b0

Browse files
Renzo-OlivaresRenzo Olivares
and
Renzo Olivares
authored
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: Renzo Olivares <[email protected]>
1 parent 3e00520 commit bd938b0

File tree

2 files changed

+62
-4
lines changed

2 files changed

+62
-4
lines changed

packages/flutter/lib/src/widgets/tap_and_drag_gestures.dart

+11-4
Original file line numberDiff line numberDiff line change
@@ -850,6 +850,7 @@ class TapAndDragGestureRecognizer extends OneSequenceGestureRecognizer with _Tap
850850
// Tap related state.
851851
bool _pastSlopTolerance = false;
852852
bool _sentTapDown = false;
853+
bool _wonArenaForPrimaryPointer = false;
853854

854855
// Primary pointer being tracked by this recognizer.
855856
int? _primaryPointer;
@@ -934,7 +935,7 @@ class TapAndDragGestureRecognizer extends OneSequenceGestureRecognizer with _Tap
934935
void handleNonAllowedPointer(PointerDownEvent event) {
935936
// There can be multiple drags simultaneously. Their effects are combined.
936937
if (event.buttons != kPrimaryButton) {
937-
if (!_sentTapDown) {
938+
if (!_wonArenaForPrimaryPointer) {
938939
super.handleNonAllowedPointer(event);
939940
}
940941
}
@@ -956,6 +957,8 @@ class TapAndDragGestureRecognizer extends OneSequenceGestureRecognizer with _Tap
956957
_checkTapDown(currentDown!);
957958
}
958959

960+
_wonArenaForPrimaryPointer = true;
961+
959962
if (_start != null) {
960963
_acceptDrag(_start!);
961964
}
@@ -976,7 +979,7 @@ class TapAndDragGestureRecognizer extends OneSequenceGestureRecognizer with _Tap
976979
case _DragState.possible:
977980
if (_pastSlopTolerance) {
978981
// This means the pointer was not accepted as a tap.
979-
if (_sentTapDown) {
982+
if (_wonArenaForPrimaryPointer) {
980983
// If the recognizer has already won the arena for the primary pointer being tracked
981984
// but the pointer has exceeded the tap tolerance, then the pointer is accepted as a
982985
// drag gesture.
@@ -1043,7 +1046,7 @@ class TapAndDragGestureRecognizer extends OneSequenceGestureRecognizer with _Tap
10431046

10441047
// This can occur when the recognizer is accepted before a [PointerMoveEvent] has been
10451048
// received that moves the pointer a sufficient global distance to be considered a drag.
1046-
if (_start != null && _sentTapDown) {
1049+
if (_start != null) {
10471050
_acceptDrag(_start!);
10481051
}
10491052
}
@@ -1085,6 +1088,9 @@ class TapAndDragGestureRecognizer extends OneSequenceGestureRecognizer with _Tap
10851088
String get debugDescription => 'tap_and_drag';
10861089

10871090
void _acceptDrag(PointerEvent event) {
1091+
if (!_wonArenaForPrimaryPointer) {
1092+
return;
1093+
}
10881094
_dragState = _DragState.accepted;
10891095
if (dragStartBehavior == DragStartBehavior.start) {
10901096
_initialPosition = _initialPosition + OffsetPair(global: event.delta, local: event.localDelta);
@@ -1138,7 +1144,7 @@ class TapAndDragGestureRecognizer extends OneSequenceGestureRecognizer with _Tap
11381144
}
11391145

11401146
void _checkTapUp(PointerUpEvent event) {
1141-
if (!_sentTapDown) {
1147+
if (!_wonArenaForPrimaryPointer) {
11421148
return;
11431149
}
11441150

@@ -1265,6 +1271,7 @@ class TapAndDragGestureRecognizer extends OneSequenceGestureRecognizer with _Tap
12651271

12661272
void _resetTaps() {
12671273
_sentTapDown = false;
1274+
_wonArenaForPrimaryPointer = false;
12681275
_primaryPointer = null;
12691276
}
12701277

packages/flutter/test/material/text_field_test.dart

+51
Original file line numberDiff line numberDiff line change
@@ -12452,6 +12452,57 @@ void main() {
1245212452
});
1245312453
});
1245412454

12455+
testWidgets('TextField does not leak touch events when deadline has exceeded', (WidgetTester tester) async {
12456+
// Regression test for https://github.com/flutter/flutter/issues/118340.
12457+
int textFieldTapCount = 0;
12458+
int prefixTapCount = 0;
12459+
int suffixTapCount = 0;
12460+
12461+
await tester.pumpWidget(
12462+
MaterialApp(
12463+
home: Scaffold(
12464+
body: TextField(
12465+
onTap: () { textFieldTapCount += 1; },
12466+
decoration: InputDecoration(
12467+
labelText: 'Label',
12468+
prefix: ElevatedButton(
12469+
onPressed: () { prefixTapCount += 1; },
12470+
child: const Text('prefix'),
12471+
),
12472+
suffix: ElevatedButton(
12473+
onPressed: () { suffixTapCount += 1; },
12474+
child: const Text('suffix'),
12475+
),
12476+
),
12477+
),
12478+
),
12479+
),
12480+
);
12481+
12482+
TestGesture gesture =
12483+
await tester.startGesture(
12484+
tester.getRect(find.text('prefix')).center,
12485+
pointer: 7,
12486+
kind: PointerDeviceKind.mouse,
12487+
);
12488+
await tester.pumpAndSettle();
12489+
await gesture.up();
12490+
expect(textFieldTapCount, 0);
12491+
expect(prefixTapCount, 1);
12492+
expect(suffixTapCount, 0);
12493+
12494+
gesture = await tester.startGesture(
12495+
tester.getRect(find.text('suffix')).center,
12496+
pointer: 7,
12497+
kind: PointerDeviceKind.mouse,
12498+
);
12499+
await tester.pumpAndSettle();
12500+
await gesture.up();
12501+
expect(textFieldTapCount, 0);
12502+
expect(prefixTapCount, 1);
12503+
expect(suffixTapCount, 1);
12504+
});
12505+
1245512506
testWidgets('prefix/suffix buttons do not leak touch events', (WidgetTester tester) async {
1245612507
// Regression test for https://github.com/flutter/flutter/issues/39376.
1245712508

0 commit comments

Comments
 (0)