_TapStatusTrackerMixin
should wait until the next PointerDownEvent
before resetting its state when the tap timer has elapsed (#129312)
`_TapStatusTrackerMixin` used by `BaseTapAndDragGestureRecognizer` should wait until the next tap down before resetting its state when the `_consecutiveTapTimer` times out. This is because `BaseTapAndDragGestureRecognizer` may not have fired its tap down/tap up event before the state has been reset preventing it from firing the tap down/tap up callbacks at all because `currentDown` and `currentUp` are reset to `null`. Fixes #129161
This commit is contained in:
parent
3df1de4c80
commit
3c366b7011
@ -539,6 +539,9 @@ mixin _TapStatusTrackerMixin on OneSequenceGestureRecognizer {
|
||||
@override
|
||||
void addAllowedPointer(PointerDownEvent event) {
|
||||
super.addAllowedPointer(event);
|
||||
if (_consecutiveTapTimer != null && !_consecutiveTapTimer!.isActive) {
|
||||
_tapTrackerReset();
|
||||
}
|
||||
if (maxConsecutiveTap == _consecutiveTapCount) {
|
||||
_tapTrackerReset();
|
||||
}
|
||||
@ -623,7 +626,7 @@ mixin _TapStatusTrackerMixin on OneSequenceGestureRecognizer {
|
||||
}
|
||||
|
||||
void _consecutiveTapTimerStart() {
|
||||
_consecutiveTapTimer ??= Timer(kDoubleTapTimeout, _tapTrackerReset);
|
||||
_consecutiveTapTimer ??= Timer(kDoubleTapTimeout, _consecutiveTapTimerTimeout);
|
||||
}
|
||||
|
||||
void _consecutiveTapTimerStop() {
|
||||
@ -633,6 +636,13 @@ mixin _TapStatusTrackerMixin on OneSequenceGestureRecognizer {
|
||||
}
|
||||
}
|
||||
|
||||
void _consecutiveTapTimerTimeout() {
|
||||
// The consecutive tap timer may time out before a tap down/tap up event is
|
||||
// fired. In this case we should not reset the tap tracker state immediately.
|
||||
// Instead we should reset the tap tracker on the next call to [addAllowedPointer],
|
||||
// if the timer is no longer active.
|
||||
}
|
||||
|
||||
void _tapTrackerReset() {
|
||||
// The timer has timed out, i.e. the time between a [PointerUpEvent] and the subsequent
|
||||
// [PointerDownEvent] exceeded the duration of [kDoubleTapTimeout], so the tap belonging
|
||||
|
@ -2147,6 +2147,52 @@ void main() {
|
||||
variant: TargetPlatformVariant.mobile(),
|
||||
);
|
||||
|
||||
testWidgets('Can select text with a mouse when wrapped in a GestureDetector with tap/double tap callbacks', (WidgetTester tester) async {
|
||||
// This is a regression test for https://github.com/flutter/flutter/issues/129161.
|
||||
final TextEditingController controller = TextEditingController();
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Material(
|
||||
child: GestureDetector(
|
||||
onTap: () {},
|
||||
onDoubleTap: () {},
|
||||
child: TextField(
|
||||
dragStartBehavior: DragStartBehavior.down,
|
||||
controller: controller,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
const String testValue = 'abc def ghi';
|
||||
await tester.enterText(find.byType(TextField), testValue);
|
||||
await skipPastScrollingAnimation(tester);
|
||||
|
||||
final Offset ePos = textOffsetToPosition(tester, testValue.indexOf('e'));
|
||||
final Offset gPos = textOffsetToPosition(tester, testValue.indexOf('g'));
|
||||
|
||||
final TestGesture gesture = await tester.startGesture(ePos, kind: PointerDeviceKind.mouse);
|
||||
await tester.pump();
|
||||
await gesture.up();
|
||||
// This is to allow the GestureArena to decide a winner between TapGestureRecognizer,
|
||||
// DoubleTapGestureRecognizer, and BaseTapAndDragGestureRecognizer.
|
||||
await tester.pumpAndSettle(kDoubleTapTimeout);
|
||||
expect(controller.selection.isCollapsed, true);
|
||||
expect(controller.selection.baseOffset, testValue.indexOf('e'));
|
||||
|
||||
await gesture.down(ePos);
|
||||
await tester.pump();
|
||||
await gesture.moveTo(gPos);
|
||||
await tester.pump();
|
||||
await gesture.up();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(controller.selection.baseOffset, testValue.indexOf('e'));
|
||||
expect(controller.selection.extentOffset, testValue.indexOf('g'));
|
||||
}, variant: TargetPlatformVariant.desktop());
|
||||
|
||||
testWidgets('Can select text by dragging with a mouse', (WidgetTester tester) async {
|
||||
final TextEditingController controller = TextEditingController();
|
||||
|
||||
|
@ -694,6 +694,44 @@ void main() {
|
||||
'panend#2']);
|
||||
});
|
||||
|
||||
// This is a regression test for https://github.com/flutter/flutter/issues/129161.
|
||||
testGesture('Beats TapGestureRecognizer and DoubleTapGestureRecognizer when the pointer has not moved and this recognizer is the first in the arena', (GestureTester tester) {
|
||||
setUpTapAndPanGestureRecognizer();
|
||||
|
||||
final TapGestureRecognizer taps = TapGestureRecognizer()
|
||||
..onTapDown = (TapDownDetails details) {
|
||||
events.add('tapdown');
|
||||
}
|
||||
..onTapUp = (TapUpDetails details) {
|
||||
events.add('tapup');
|
||||
}
|
||||
..onTapCancel = () {
|
||||
events.add('tapscancel');
|
||||
};
|
||||
|
||||
final DoubleTapGestureRecognizer doubleTaps = DoubleTapGestureRecognizer()
|
||||
..onDoubleTapDown = (TapDownDetails details) {
|
||||
events.add('doubletapdown');
|
||||
}
|
||||
..onDoubleTap = () {
|
||||
events.add('doubletapup');
|
||||
}
|
||||
..onDoubleTapCancel = () {
|
||||
events.add('doubletapcancel');
|
||||
};
|
||||
|
||||
tapAndDrag.addPointer(down1);
|
||||
taps.addPointer(down1);
|
||||
doubleTaps.addPointer(down1);
|
||||
tester.closeArena(1);
|
||||
tester.route(down1);
|
||||
tester.route(up1);
|
||||
GestureBinding.instance.gestureArena.sweep(1);
|
||||
// Wait for GestureArena to resolve itself.
|
||||
tester.async.elapse(kDoubleTapTimeout);
|
||||
expect(events, <String>['down#1', 'up#1']);
|
||||
});
|
||||
|
||||
testGesture('Beats TapGestureRecognizer when the pointer has not moved and this recognizer is the first in the arena', (GestureTester tester) {
|
||||
setUpTapAndPanGestureRecognizer();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user