| // Copyright 2014 The Flutter 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/gestures.dart'; |
| import 'package:flutter_test/flutter_test.dart'; |
| |
| import 'gesture_tester.dart'; |
| |
| // Down/move/up pair 1: normal tap sequence |
| const PointerDownEvent down = PointerDownEvent( |
| pointer: 5, |
| position: Offset(10, 10), |
| ); |
| |
| const PointerUpEvent up = PointerUpEvent( |
| pointer: 5, |
| position: Offset(11, 9), |
| ); |
| |
| const PointerMoveEvent move = PointerMoveEvent( |
| pointer: 5, |
| position: Offset(100, 200), |
| ); |
| |
| // Down/up pair 2: normal tap sequence far away from pair 1 |
| const PointerDownEvent down2 = PointerDownEvent( |
| pointer: 6, |
| position: Offset(10, 10), |
| ); |
| |
| const PointerUpEvent up2 = PointerUpEvent( |
| pointer: 6, |
| position: Offset(11, 9), |
| ); |
| |
| // Down/up pair 3: tap sequence with secondary button |
| const PointerDownEvent down3 = PointerDownEvent( |
| pointer: 7, |
| position: Offset(30, 30), |
| buttons: kSecondaryButton, |
| ); |
| |
| const PointerUpEvent up3 = PointerUpEvent( |
| pointer: 7, |
| position: Offset(31, 29), |
| ); |
| |
| // Down/up pair 4: tap sequence with tertiary button |
| const PointerDownEvent down4 = PointerDownEvent( |
| pointer: 8, |
| position: Offset(42, 24), |
| buttons: kTertiaryButton, |
| ); |
| |
| const PointerUpEvent up4 = PointerUpEvent( |
| pointer: 8, |
| position: Offset(43, 23), |
| ); |
| |
| void main() { |
| setUp(ensureGestureBinding); |
| |
| group('Long press', () { |
| late LongPressGestureRecognizer gesture; |
| late List<String> recognized; |
| |
| void setUpHandlers() { |
| gesture |
| ..onLongPressDown = (LongPressDownDetails details) { |
| recognized.add('down'); |
| } |
| ..onLongPressCancel = () { |
| recognized.add('cancel'); |
| } |
| ..onLongPress = () { |
| recognized.add('start'); |
| } |
| ..onLongPressMoveUpdate = (LongPressMoveUpdateDetails details) { |
| recognized.add('move'); |
| } |
| ..onLongPressUp = () { |
| recognized.add('end'); |
| }; |
| } |
| |
| setUp(() { |
| recognized = <String>[]; |
| gesture = LongPressGestureRecognizer(); |
| setUpHandlers(); |
| }); |
| |
| testGesture('Should recognize long press', (GestureTester tester) { |
| gesture.addPointer(down); |
| tester.closeArena(5); |
| expect(recognized, const <String>[]); |
| tester.route(down); |
| expect(recognized, const <String>['down']); |
| tester.async.elapse(const Duration(milliseconds: 300)); |
| expect(recognized, const <String>['down']); |
| tester.async.elapse(const Duration(milliseconds: 700)); |
| expect(recognized, const <String>['down', 'start']); |
| gesture.dispose(); |
| expect(recognized, const <String>['down', 'start']); |
| }); |
| |
| testGesture('Should recognize long press with altered duration', (GestureTester tester) { |
| gesture = LongPressGestureRecognizer(duration: const Duration(milliseconds: 100)); |
| setUpHandlers(); |
| gesture.addPointer(down); |
| tester.closeArena(5); |
| expect(recognized, const <String>[]); |
| tester.route(down); |
| expect(recognized, const <String>['down']); |
| tester.async.elapse(const Duration(milliseconds: 50)); |
| expect(recognized, const <String>['down']); |
| tester.async.elapse(const Duration(milliseconds: 50)); |
| expect(recognized, const <String>['down', 'start']); |
| gesture.dispose(); |
| expect(recognized, const <String>['down', 'start']); |
| }); |
| |
| testGesture('Up cancels long press', (GestureTester tester) { |
| gesture.addPointer(down); |
| tester.closeArena(5); |
| expect(recognized, const <String>[]); |
| tester.route(down); |
| expect(recognized, const <String>['down']); |
| tester.async.elapse(const Duration(milliseconds: 300)); |
| expect(recognized, const <String>['down']); |
| tester.route(up); |
| expect(recognized, const <String>['down', 'cancel']); |
| tester.async.elapse(const Duration(seconds: 1)); |
| gesture.dispose(); |
| expect(recognized, const <String>['down', 'cancel']); |
| }); |
| |
| testGesture('Moving before accept cancels', (GestureTester tester) { |
| gesture.addPointer(down); |
| tester.closeArena(5); |
| expect(recognized, const <String>[]); |
| tester.route(down); |
| expect(recognized, const <String>['down']); |
| tester.async.elapse(const Duration(milliseconds: 300)); |
| expect(recognized, const <String>['down']); |
| tester.route(move); |
| expect(recognized, const <String>['down', 'cancel']); |
| tester.async.elapse(const Duration(seconds: 1)); |
| tester.route(up); |
| tester.async.elapse(const Duration(milliseconds: 300)); |
| expect(recognized, const <String>['down', 'cancel']); |
| gesture.dispose(); |
| expect(recognized, const <String>['down', 'cancel']); |
| }); |
| |
| testGesture('Moving after accept is ok', (GestureTester tester) { |
| gesture.addPointer(down); |
| tester.closeArena(5); |
| expect(recognized, const <String>[]); |
| tester.route(down); |
| expect(recognized, const <String>['down']); |
| tester.async.elapse(const Duration(seconds: 1)); |
| expect(recognized, const <String>['down', 'start']); |
| tester.route(move); |
| expect(recognized, const <String>['down', 'start', 'move']); |
| tester.route(up); |
| expect(recognized, const <String>['down', 'start', 'move', 'end']); |
| tester.async.elapse(const Duration(milliseconds: 300)); |
| expect(recognized, const <String>['down', 'start', 'move', 'end']); |
| gesture.dispose(); |
| expect(recognized, const <String>['down', 'start', 'move', 'end']); |
| }); |
| |
| testGesture('Should recognize both tap down and long press', (GestureTester tester) { |
| final TapGestureRecognizer tap = TapGestureRecognizer(); |
| tap.onTapDown = (_) { |
| recognized.add('tap_down'); |
| }; |
| |
| tap.addPointer(down); |
| gesture.addPointer(down); |
| tester.closeArena(5); |
| expect(recognized, const <String>[]); |
| tester.route(down); |
| expect(recognized, const <String>['down']); |
| tester.async.elapse(const Duration(milliseconds: 300)); |
| expect(recognized, const <String>['down', 'tap_down']); |
| tester.async.elapse(const Duration(milliseconds: 700)); |
| expect(recognized, const <String>['down', 'tap_down', 'start']); |
| tap.dispose(); |
| gesture.dispose(); |
| expect(recognized, const <String>['down', 'tap_down', 'start']); |
| }); |
| |
| testGesture('Drag start delayed by microtask', (GestureTester tester) { |
| final HorizontalDragGestureRecognizer drag = HorizontalDragGestureRecognizer(); |
| bool isDangerousStack = false; |
| drag.onStart = (DragStartDetails details) { |
| expect(isDangerousStack, isFalse); |
| recognized.add('drag_start'); |
| }; |
| |
| drag.addPointer(down); |
| gesture.addPointer(down); |
| tester.closeArena(5); |
| expect(recognized, const <String>[]); |
| tester.route(down); |
| expect(recognized, const <String>['down']); |
| tester.async.elapse(const Duration(milliseconds: 300)); |
| expect(recognized, const <String>['down']); |
| isDangerousStack = true; |
| gesture.dispose(); |
| isDangerousStack = false; |
| expect(recognized, const <String>['down', 'cancel']); |
| tester.async.flushMicrotasks(); |
| expect(recognized, const <String>['down', 'cancel', 'drag_start']); |
| drag.dispose(); |
| }); |
| |
| testGesture('Should recognize long press up', (GestureTester tester) { |
| gesture.addPointer(down); |
| tester.closeArena(5); |
| expect(recognized, const <String>[]); |
| tester.route(down); // kLongPressTimeout = 500; |
| expect(recognized, const <String>['down']); |
| tester.async.elapse(const Duration(milliseconds: 300)); |
| expect(recognized, const <String>['down']); |
| tester.async.elapse(const Duration(milliseconds: 700)); |
| expect(recognized, const <String>['down', 'start']); |
| tester.route(up); |
| expect(recognized, const <String>['down', 'start', 'end']); |
| gesture.dispose(); |
| expect(recognized, const <String>['down', 'start', 'end']); |
| }); |
| |
| testGesture('Should not recognize long press with more than one buttons', (GestureTester tester) { |
| gesture.addPointer(const PointerDownEvent( |
| pointer: 5, |
| kind: PointerDeviceKind.mouse, |
| buttons: kSecondaryMouseButton | kTertiaryButton, |
| position: Offset(10, 10), |
| )); |
| tester.closeArena(5); |
| expect(recognized, const <String>[]); |
| tester.route(down); |
| expect(recognized, const <String>[]); |
| tester.async.elapse(const Duration(milliseconds: 1000)); |
| expect(recognized, const <String>[]); |
| tester.route(up); |
| expect(recognized, const <String>[]); |
| gesture.dispose(); |
| expect(recognized, const <String>[]); |
| }); |
| |
| testGesture('Should cancel long press when buttons change before acceptance', (GestureTester tester) { |
| gesture.addPointer(down); |
| tester.closeArena(5); |
| expect(recognized, const <String>[]); |
| tester.route(down); |
| expect(recognized, const <String>['down']); |
| tester.async.elapse(const Duration(milliseconds: 300)); |
| expect(recognized, const <String>['down']); |
| tester.route(const PointerMoveEvent( |
| pointer: 5, |
| kind: PointerDeviceKind.mouse, |
| buttons: kTertiaryButton, |
| position: Offset(10, 10), |
| )); |
| expect(recognized, const <String>['down', 'cancel']); |
| tester.async.elapse(const Duration(milliseconds: 700)); |
| expect(recognized, const <String>['down', 'cancel']); |
| tester.route(up); |
| expect(recognized, const <String>['down', 'cancel']); |
| gesture.dispose(); |
| expect(recognized, const <String>['down', 'cancel']); |
| }); |
| |
| testGesture('non-allowed pointer does not inadvertently reset the recognizer', (GestureTester tester) { |
| gesture = LongPressGestureRecognizer(kind: PointerDeviceKind.touch); |
| setUpHandlers(); |
| |
| // Accept a long-press gesture |
| gesture.addPointer(down); |
| tester.closeArena(5); |
| tester.route(down); |
| tester.async.elapse(const Duration(milliseconds: 500)); |
| expect(recognized, const <String>['down', 'start']); |
| |
| // Add a non-allowed pointer (doesn't match the kind filter) |
| gesture.addPointer(const PointerDownEvent( |
| pointer: 101, |
| kind: PointerDeviceKind.mouse, |
| position: Offset(10, 10), |
| )); |
| expect(recognized, const <String>['down', 'start']); |
| |
| // Moving the primary pointer should result in a normal event |
| tester.route(const PointerMoveEvent( |
| pointer: 5, |
| position: Offset(15, 15), |
| )); |
| expect(recognized, const <String>['down', 'start', 'move']); |
| gesture.dispose(); |
| }); |
| }); |
| |
| group('long press drag', () { |
| late LongPressGestureRecognizer gesture; |
| Offset? longPressDragUpdate; |
| late List<String> recognized; |
| |
| void setUpHandlers() { |
| gesture |
| ..onLongPressDown = (LongPressDownDetails details) { |
| recognized.add('down'); |
| } |
| ..onLongPressCancel = () { |
| recognized.add('cancel'); |
| } |
| ..onLongPress = () { |
| recognized.add('start'); |
| } |
| ..onLongPressMoveUpdate = (LongPressMoveUpdateDetails details) { |
| recognized.add('move'); |
| longPressDragUpdate = details.globalPosition; |
| } |
| ..onLongPressUp = () { |
| recognized.add('end'); |
| }; |
| } |
| |
| setUp(() { |
| gesture = LongPressGestureRecognizer(); |
| setUpHandlers(); |
| recognized = <String>[]; |
| }); |
| |
| testGesture('Should recognize long press down', (GestureTester tester) { |
| gesture.addPointer(down); |
| tester.closeArena(5); |
| expect(recognized, const <String>[]); |
| tester.route(down); |
| expect(recognized, const <String>['down']); |
| tester.async.elapse(const Duration(milliseconds: 300)); |
| expect(recognized, const <String>['down']); |
| tester.async.elapse(const Duration(milliseconds: 700)); |
| expect(recognized, const <String>['down', 'start']); |
| gesture.dispose(); |
| expect(recognized, const <String>['down', 'start']); |
| }); |
| |
| testGesture('Short up cancels long press', (GestureTester tester) { |
| gesture.addPointer(down); |
| tester.closeArena(5); |
| expect(recognized, const <String>[]); |
| tester.route(down); |
| expect(recognized, const <String>['down']); |
| tester.async.elapse(const Duration(milliseconds: 300)); |
| expect(recognized, const <String>['down']); |
| tester.route(up); |
| expect(recognized, const <String>['down', 'cancel']); |
| tester.async.elapse(const Duration(seconds: 1)); |
| expect(recognized, const <String>['down', 'cancel']); |
| gesture.dispose(); |
| expect(recognized, const <String>['down', 'cancel']); |
| }); |
| |
| testGesture('Moving before accept cancels', (GestureTester tester) { |
| gesture.addPointer(down); |
| tester.closeArena(5); |
| expect(recognized, const <String>[]); |
| tester.route(down); |
| expect(recognized, const <String>['down']); |
| tester.async.elapse(const Duration(milliseconds: 300)); |
| expect(recognized, const <String>['down']); |
| tester.route(move); |
| expect(recognized, const <String>['down', 'cancel']); |
| tester.async.elapse(const Duration(seconds: 1)); |
| tester.route(up); |
| tester.async.elapse(const Duration(milliseconds: 300)); |
| expect(recognized, const <String>['down', 'cancel']); |
| gesture.dispose(); |
| expect(recognized, const <String>['down', 'cancel']); |
| }); |
| |
| testGesture('Moving after accept does not cancel', (GestureTester tester) { |
| gesture.addPointer(down); |
| tester.closeArena(5); |
| expect(recognized, const <String>[]); |
| tester.route(down); |
| expect(recognized, const <String>['down']); |
| tester.async.elapse(const Duration(seconds: 1)); |
| expect(recognized, const <String>['down', 'start']); |
| tester.route(move); |
| expect(recognized, const <String>['down', 'start', 'move']); |
| expect(longPressDragUpdate, const Offset(100, 200)); |
| tester.route(up); |
| tester.async.elapse(const Duration(milliseconds: 300)); |
| expect(recognized, const <String>['down', 'start', 'move', 'end']); |
| gesture.dispose(); |
| expect(recognized, const <String>['down', 'start', 'move', 'end']); |
| }); |
| }); |
| |
| group('Enforce consistent-button restriction:', () { |
| // In sequence between `down` and `up` but with buttons changed |
| const PointerMoveEvent moveR = PointerMoveEvent( |
| pointer: 5, |
| buttons: kSecondaryButton, |
| position: Offset(10, 10), |
| ); |
| |
| late LongPressGestureRecognizer gesture; |
| final List<String> recognized = <String>[]; |
| |
| setUp(() { |
| gesture = LongPressGestureRecognizer() |
| ..onLongPressDown = (LongPressDownDetails details) { |
| recognized.add('down'); |
| } |
| ..onLongPressCancel = () { |
| recognized.add('cancel'); |
| } |
| ..onLongPressStart = (LongPressStartDetails details) { |
| recognized.add('start'); |
| } |
| ..onLongPressEnd = (LongPressEndDetails details) { |
| recognized.add('end'); |
| }; |
| }); |
| |
| tearDown(() { |
| gesture.dispose(); |
| recognized.clear(); |
| }); |
| |
| testGesture('Should cancel long press when buttons change before acceptance', (GestureTester tester) { |
| // First press |
| gesture.addPointer(down); |
| tester.closeArena(down.pointer); |
| tester.route(down); |
| tester.async.elapse(const Duration(milliseconds: 300)); |
| tester.route(moveR); |
| expect(recognized, const <String>['down', 'cancel']); |
| tester.async.elapse(const Duration(milliseconds: 700)); |
| tester.route(up); |
| expect(recognized, const <String>['down', 'cancel']); |
| }); |
| |
| testGesture('Buttons change before acceptance should not prevent the next long press', (GestureTester tester) { |
| // First press |
| gesture.addPointer(down); |
| tester.closeArena(down.pointer); |
| tester.route(down); |
| expect(recognized, <String>['down']); |
| tester.async.elapse(const Duration(milliseconds: 300)); |
| tester.route(moveR); |
| expect(recognized, <String>['down', 'cancel']); |
| tester.async.elapse(const Duration(milliseconds: 700)); |
| tester.route(up); |
| recognized.clear(); |
| |
| // Second press |
| gesture.addPointer(down2); |
| tester.closeArena(down2.pointer); |
| tester.route(down2); |
| expect(recognized, <String>['down']); |
| tester.async.elapse(const Duration(milliseconds: 1000)); |
| expect(recognized, <String>['down', 'start']); |
| recognized.clear(); |
| |
| tester.route(up2); |
| expect(recognized, <String>['end']); |
| }); |
| |
| testGesture('Should cancel long press when buttons change after acceptance', (GestureTester tester) { |
| // First press |
| gesture.addPointer(down); |
| tester.closeArena(down.pointer); |
| tester.route(down); |
| tester.async.elapse(const Duration(milliseconds: 1000)); |
| expect(recognized, <String>['down', 'start']); |
| recognized.clear(); |
| |
| tester.route(moveR); |
| expect(recognized, <String>[]); |
| tester.route(up); |
| expect(recognized, <String>[]); |
| }); |
| |
| testGesture('Buttons change after acceptance should not prevent the next long press', (GestureTester tester) { |
| // First press |
| gesture.addPointer(down); |
| tester.closeArena(down.pointer); |
| tester.route(down); |
| tester.async.elapse(const Duration(milliseconds: 1000)); |
| tester.route(moveR); |
| tester.route(up); |
| recognized.clear(); |
| |
| // Second press |
| gesture.addPointer(down2); |
| tester.closeArena(down2.pointer); |
| tester.route(down2); |
| tester.async.elapse(const Duration(milliseconds: 1000)); |
| expect(recognized, <String>['down', 'start']); |
| recognized.clear(); |
| |
| tester.route(up2); |
| expect(recognized, <String>['end']); |
| }); |
| }); |
| |
| testGesture('Can filter long press based on device kind', (GestureTester tester) { |
| final LongPressGestureRecognizer mouseLongPress = LongPressGestureRecognizer(kind: PointerDeviceKind.mouse); |
| |
| bool mouseLongPressDown = false; |
| mouseLongPress.onLongPress = () { |
| mouseLongPressDown = true; |
| }; |
| |
| const PointerDownEvent mouseDown = PointerDownEvent( |
| pointer: 5, |
| position: Offset(10, 10), |
| kind: PointerDeviceKind.mouse, |
| ); |
| const PointerDownEvent touchDown = PointerDownEvent( |
| pointer: 5, |
| position: Offset(10, 10), |
| ); |
| |
| // Touch events shouldn't be recognized. |
| mouseLongPress.addPointer(touchDown); |
| tester.closeArena(5); |
| expect(mouseLongPressDown, isFalse); |
| tester.route(touchDown); |
| expect(mouseLongPressDown, isFalse); |
| tester.async.elapse(const Duration(seconds: 2)); |
| expect(mouseLongPressDown, isFalse); |
| |
| // Mouse events are still recognized. |
| mouseLongPress.addPointer(mouseDown); |
| tester.closeArena(5); |
| expect(mouseLongPressDown, isFalse); |
| tester.route(mouseDown); |
| expect(mouseLongPressDown, isFalse); |
| tester.async.elapse(const Duration(seconds: 2)); |
| expect(mouseLongPressDown, isTrue); |
| |
| mouseLongPress.dispose(); |
| }); |
| |
| group('Recognizers listening on different buttons do not form competition:', () { |
| // This test is assisted by tap recognizers. If a tap gesture has |
| // no competing recognizers, a pointer down event triggers its onTapDown |
| // immediately; if there are competitors, onTapDown is triggered after a |
| // timeout. |
| // The following tests make sure that long press recognizers do not form |
| // competition with a tap gesture recognizer listening on a different button. |
| |
| final List<String> recognized = <String>[]; |
| late TapGestureRecognizer tapPrimary; |
| late TapGestureRecognizer tapSecondary; |
| late LongPressGestureRecognizer longPress; |
| setUp(() { |
| tapPrimary = TapGestureRecognizer() |
| ..onTapDown = (TapDownDetails details) { |
| recognized.add('tapPrimary'); |
| }; |
| tapSecondary = TapGestureRecognizer() |
| ..onSecondaryTapDown = (TapDownDetails details) { |
| recognized.add('tapSecondary'); |
| }; |
| longPress = LongPressGestureRecognizer() |
| ..onLongPressStart = (_) { |
| recognized.add('longPress'); |
| }; |
| }); |
| |
| tearDown(() { |
| recognized.clear(); |
| tapPrimary.dispose(); |
| tapSecondary.dispose(); |
| longPress.dispose(); |
| }); |
| |
| testGesture('A primary long press recognizer does not form competition with a secondary tap recognizer', (GestureTester tester) { |
| longPress.addPointer(down3); |
| tapSecondary.addPointer(down3); |
| tester.closeArena(down3.pointer); |
| |
| tester.route(down3); |
| expect(recognized, <String>['tapSecondary']); |
| }); |
| |
| testGesture('A primary long press recognizer forms competition with a primary tap recognizer', (GestureTester tester) { |
| longPress.addPointer(down); |
| tapPrimary.addPointer(down); |
| tester.closeArena(down.pointer); |
| |
| tester.route(down); |
| expect(recognized, <String>[]); |
| |
| tester.route(up); |
| expect(recognized, <String>['tapPrimary']); |
| }); |
| }); |
| |
| testGesture('A secondary long press should not trigger primary', (GestureTester tester) { |
| final List<String> recognized = <String>[]; |
| final LongPressGestureRecognizer longPress = LongPressGestureRecognizer() |
| ..onLongPressStart = (LongPressStartDetails details) { |
| recognized.add('primaryStart'); |
| } |
| ..onLongPress = () { |
| recognized.add('primary'); |
| } |
| ..onLongPressMoveUpdate = (LongPressMoveUpdateDetails details) { |
| recognized.add('primaryUpdate'); |
| } |
| ..onLongPressEnd = (LongPressEndDetails details) { |
| recognized.add('primaryEnd'); |
| } |
| ..onLongPressUp = () { |
| recognized.add('primaryUp'); |
| }; |
| |
| const PointerDownEvent down2 = PointerDownEvent( |
| pointer: 2, |
| buttons: kSecondaryButton, |
| position: Offset(30.0, 30.0), |
| ); |
| |
| const PointerMoveEvent move2 = PointerMoveEvent( |
| pointer: 2, |
| buttons: kSecondaryButton, |
| position: Offset(100, 200), |
| ); |
| |
| const PointerUpEvent up2 = PointerUpEvent( |
| pointer: 2, |
| position: Offset(100, 201), |
| ); |
| |
| longPress.addPointer(down2); |
| tester.closeArena(2); |
| tester.route(down2); |
| tester.async.elapse(const Duration(milliseconds: 700)); |
| tester.route(move2); |
| tester.route(up2); |
| expect(recognized, <String>[]); |
| longPress.dispose(); |
| recognized.clear(); |
| }); |
| |
| testGesture('A tertiary long press should not trigger primary or secondary', (GestureTester tester) { |
| final List<String> recognized = <String>[]; |
| final LongPressGestureRecognizer longPress = LongPressGestureRecognizer() |
| ..onLongPressStart = (LongPressStartDetails details) { |
| recognized.add('primaryStart'); |
| } |
| ..onLongPress = () { |
| recognized.add('primary'); |
| } |
| ..onLongPressMoveUpdate = (LongPressMoveUpdateDetails details) { |
| recognized.add('primaryUpdate'); |
| } |
| ..onLongPressEnd = (LongPressEndDetails details) { |
| recognized.add('primaryEnd'); |
| } |
| ..onLongPressUp = () { |
| recognized.add('primaryUp'); |
| } |
| ..onSecondaryLongPressStart = (LongPressStartDetails details) { |
| recognized.add('secondaryStart'); |
| } |
| ..onSecondaryLongPress = () { |
| recognized.add('secondary'); |
| } |
| ..onSecondaryLongPressMoveUpdate = (LongPressMoveUpdateDetails details) { |
| recognized.add('secondaryUpdate'); |
| } |
| ..onSecondaryLongPressEnd = (LongPressEndDetails details) { |
| recognized.add('secondaryEnd'); |
| } |
| ..onSecondaryLongPressUp = () { |
| recognized.add('secondaryUp'); |
| }; |
| |
| const PointerDownEvent down2 = PointerDownEvent( |
| pointer: 2, |
| buttons: kTertiaryButton, |
| position: Offset(30.0, 30.0), |
| ); |
| |
| const PointerMoveEvent move2 = PointerMoveEvent( |
| pointer: 2, |
| buttons: kTertiaryButton, |
| position: Offset(100, 200), |
| ); |
| |
| const PointerUpEvent up2 = PointerUpEvent( |
| pointer: 2, |
| position: Offset(100, 201), |
| ); |
| |
| longPress.addPointer(down2); |
| tester.closeArena(2); |
| tester.route(down2); |
| tester.async.elapse(const Duration(milliseconds: 700)); |
| tester.route(move2); |
| tester.route(up2); |
| expect(recognized, <String>[]); |
| longPress.dispose(); |
| recognized.clear(); |
| }); |
| |
| testWidgets('LongPressGestureRecognizer asserts when kind and supportedDevices are both set', (WidgetTester tester) async { |
| expect( |
| () { |
| LongPressGestureRecognizer( |
| kind: PointerDeviceKind.touch, |
| supportedDevices: <PointerDeviceKind>{ PointerDeviceKind.touch }, |
| ); |
| }, |
| throwsA( |
| isA<AssertionError>().having((AssertionError error) => error.toString(), |
| 'description', contains('kind == null || supportedDevices == null')), |
| ), |
| ); |
| }); |
| } |