Short circuit _checkUp if tap callbacks were triggered by resolve. (#12521)
This commit is contained in:
parent
bd3e91ed98
commit
3d7a4eed44
@ -2,6 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'arena.dart';
|
||||
import 'constants.dart';
|
||||
import 'events.dart';
|
||||
@ -90,6 +92,8 @@ class TapGestureRecognizer extends PrimaryPointerGestureRecognizer {
|
||||
if (event is PointerUpEvent) {
|
||||
_finalPosition = event.position;
|
||||
_checkUp();
|
||||
} else if (event is PointerCancelEvent) {
|
||||
_reset();
|
||||
}
|
||||
}
|
||||
|
||||
@ -143,6 +147,14 @@ class TapGestureRecognizer extends PrimaryPointerGestureRecognizer {
|
||||
void _checkUp() {
|
||||
if (_wonArenaForPrimaryPointer && _finalPosition != null) {
|
||||
resolve(GestureDisposition.accepted);
|
||||
if (!_wonArenaForPrimaryPointer || _finalPosition == null) {
|
||||
// It is possible that resolve has just recursively called _checkUp
|
||||
// (see https://github.com/flutter/flutter/issues/12470).
|
||||
// In that case _wonArenaForPrimaryPointer will be false (as _checkUp
|
||||
// calls _reset) and we return here to avoid double invocation of the
|
||||
// tap callbacks.
|
||||
return;
|
||||
}
|
||||
if (onTapUp != null)
|
||||
invokeCallback<Null>('onTapUp', () { onTapUp(new TapUpDetails(globalPosition: _finalPosition)); });
|
||||
if (onTap != null)
|
||||
@ -159,4 +171,12 @@ class TapGestureRecognizer extends PrimaryPointerGestureRecognizer {
|
||||
|
||||
@override
|
||||
String get debugDescription => 'tap';
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder description) {
|
||||
super.debugFillProperties(description);
|
||||
description.add(new FlagProperty('wonArenaForPrimaryPointer', value: _wonArenaForPrimaryPointer, ifTrue: 'won arena'));
|
||||
description.add(new DiagnosticsProperty<Offset>('finalPosition', _finalPosition, defaultValue: null));
|
||||
description.add(new FlagProperty('sentTapDown', value: _sentTapDown, ifTrue: 'sent tap down'));
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ void main() {
|
||||
GestureBinding.instance.gestureArena.sweep(1);
|
||||
expect(log, hasLength(2));
|
||||
expect(log[0], equalsIgnoringHashCodes('Gesture arena 1 ❙ Sweeping with 1 member.'));
|
||||
expect(log[1], equalsIgnoringHashCodes('Gesture arena 1 ❙ Winner: TapGestureRecognizer#00000(state: ready)'));
|
||||
expect(log[1], equalsIgnoringHashCodes('Gesture arena 1 ❙ Winner: TapGestureRecognizer#00000(state: ready, finalPosition: Offset(12.0, 8.0))'));
|
||||
log.clear();
|
||||
|
||||
tap.dispose();
|
||||
@ -83,9 +83,9 @@ void main() {
|
||||
|
||||
GestureBinding.instance.gestureArena.sweep(1);
|
||||
expect(log, hasLength(3));
|
||||
expect(log[0], equalsIgnoringHashCodes('TapGestureRecognizer#00000(state: ready) calling onTapDown callback.'));
|
||||
expect(log[1], equalsIgnoringHashCodes('TapGestureRecognizer#00000(state: ready) calling onTapUp callback.'));
|
||||
expect(log[2], equalsIgnoringHashCodes('TapGestureRecognizer#00000(state: ready) calling onTap callback.'));
|
||||
expect(log[0], equalsIgnoringHashCodes('TapGestureRecognizer#00000(state: ready, finalPosition: Offset(12.0, 8.0)) calling onTapDown callback.'));
|
||||
expect(log[1], equalsIgnoringHashCodes('TapGestureRecognizer#00000(state: ready, won arena, finalPosition: Offset(12.0, 8.0), sent tap down) calling onTapUp callback.'));
|
||||
expect(log[2], equalsIgnoringHashCodes('TapGestureRecognizer#00000(state: ready, won arena, finalPosition: Offset(12.0, 8.0), sent tap down) calling onTap callback.'));
|
||||
log.clear();
|
||||
|
||||
tap.dispose();
|
||||
@ -132,10 +132,10 @@ void main() {
|
||||
GestureBinding.instance.gestureArena.sweep(1);
|
||||
expect(log, hasLength(5));
|
||||
expect(log[0], equalsIgnoringHashCodes('Gesture arena 1 ❙ Sweeping with 1 member.'));
|
||||
expect(log[1], equalsIgnoringHashCodes('Gesture arena 1 ❙ Winner: TapGestureRecognizer#00000(state: ready)'));
|
||||
expect(log[2], equalsIgnoringHashCodes(' ❙ TapGestureRecognizer#00000(state: ready) calling onTapDown callback.'));
|
||||
expect(log[3], equalsIgnoringHashCodes(' ❙ TapGestureRecognizer#00000(state: ready) calling onTapUp callback.'));
|
||||
expect(log[4], equalsIgnoringHashCodes(' ❙ TapGestureRecognizer#00000(state: ready) calling onTap callback.'));
|
||||
expect(log[1], equalsIgnoringHashCodes('Gesture arena 1 ❙ Winner: TapGestureRecognizer#00000(state: ready, finalPosition: Offset(12.0, 8.0))'));
|
||||
expect(log[2], equalsIgnoringHashCodes(' ❙ TapGestureRecognizer#00000(state: ready, finalPosition: Offset(12.0, 8.0)) calling onTapDown callback.'));
|
||||
expect(log[3], equalsIgnoringHashCodes(' ❙ TapGestureRecognizer#00000(state: ready, won arena, finalPosition: Offset(12.0, 8.0), sent tap down) calling onTapUp callback.'));
|
||||
expect(log[4], equalsIgnoringHashCodes(' ❙ TapGestureRecognizer#00000(state: ready, won arena, finalPosition: Offset(12.0, 8.0), sent tap down) calling onTap callback.'));
|
||||
log.clear();
|
||||
|
||||
tap.dispose();
|
||||
@ -145,4 +145,13 @@ void main() {
|
||||
debugPrintRecognizerCallbacksTrace = false;
|
||||
debugPrint = oldCallback;
|
||||
});
|
||||
|
||||
test('TapGestureRecognizer _sentTapDown toString', () {
|
||||
final TapGestureRecognizer tap = new TapGestureRecognizer();
|
||||
expect(tap.toString(), equalsIgnoringHashCodes('TapGestureRecognizer#00000(state: ready)'));
|
||||
final PointerEvent event = const PointerDownEvent(pointer: 1, position: const Offset(10.0, 10.0));
|
||||
tap.addPointer(event);
|
||||
tap.didExceedDeadline();
|
||||
expect(tap.toString(), equalsIgnoringHashCodes('TapGestureRecognizer#00000(state: possible, sent tap down)'));
|
||||
});
|
||||
}
|
||||
|
@ -0,0 +1,47 @@
|
||||
// Copyright 2017 The Chromium 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_test/flutter_test.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('onTap detection with canceled pointer and a drag listener', (WidgetTester tester) async {
|
||||
int detector1TapCount = 0;
|
||||
int detector2TapCount = 0;
|
||||
|
||||
final Widget widget = new GestureDetector(
|
||||
child: new Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
new GestureDetector(
|
||||
onTap: () { detector1TapCount += 1; },
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: const SizedBox(width: 200.0, height: 200.0),
|
||||
),
|
||||
new GestureDetector(
|
||||
onTap: () { detector2TapCount += 1; },
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: const SizedBox(width: 200.0, height: 200.0)
|
||||
)
|
||||
],
|
||||
)
|
||||
);
|
||||
|
||||
await tester.pumpWidget(widget);
|
||||
|
||||
// The following pointer event sequence was causing the issue described
|
||||
// in https://github.com/flutter/flutter/issues/12470 by triggering 2 tap
|
||||
// events on the second detector.
|
||||
final TestGesture gesture1 = await tester.startGesture(const Offset(400.0, 10.0));
|
||||
final TestGesture gesture2 = await tester.startGesture(const Offset(400.0, 210.0));
|
||||
await gesture1.up();
|
||||
await gesture2.cancel();
|
||||
final TestGesture gesture3 = await tester.startGesture(const Offset(400.0, 250.0));
|
||||
await gesture3.up();
|
||||
|
||||
expect(detector1TapCount, 1);
|
||||
expect(detector2TapCount, 1);
|
||||
});
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user