mirror of
https://github.com/flutter/flutter
synced 2024-10-13 03:32:55 +00:00
Add onLongPressDown, onLongPressCancel (#81260)
This adds support for GestureDetector.onLongPressDown and GestureDetector.onLongPressCancel, allowing callers to register interest in the initial pointer contact that might turn into a long-press (and the associated cancel event if the gesture loses).
This commit is contained in:
parent
99f19d05c3
commit
91de4bd3e3
|
@ -9,16 +9,50 @@ import 'events.dart';
|
|||
import 'recognizer.dart';
|
||||
import 'velocity_tracker.dart';
|
||||
|
||||
/// Callback signature for [LongPressGestureRecognizer.onLongPressDown].
|
||||
///
|
||||
/// Called when a pointer that might cause a long-press has contacted the
|
||||
/// screen. The position at which the pointer contacted the screen is available
|
||||
/// in the `details`.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [GestureDetector.onLongPressDown], which matches this signature.
|
||||
/// * [GestureLongPressStartCallback], the signature that gets called when the
|
||||
/// pointer has been in contact with the screen long enough to be considered
|
||||
/// a long-press.
|
||||
typedef GestureLongPressDownCallback = void Function(LongPressDownDetails details);
|
||||
|
||||
/// Callback signature for [LongPressGestureRecognizer.onLongPressCancel].
|
||||
///
|
||||
/// Called when the pointer that previously triggered a
|
||||
/// [GestureLongPressDownCallback] will not end up causing a long-press.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [GestureDetector.onLongPressCancel], which matches this signature.
|
||||
typedef GestureLongPressCancelCallback = void Function();
|
||||
|
||||
/// Callback signature for [LongPressGestureRecognizer.onLongPress].
|
||||
///
|
||||
/// Called when a pointer has remained in contact with the screen at the
|
||||
/// same location for a long period of time.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [GestureDetector.onLongPress], which matches this signature.
|
||||
/// * [GestureLongPressStartCallback], which is the same signature but with
|
||||
/// details of where the long press occurred.
|
||||
typedef GestureLongPressCallback = void Function();
|
||||
|
||||
/// Callback signature for [LongPressGestureRecognizer.onLongPressUp].
|
||||
///
|
||||
/// Called when a pointer stops contacting the screen after a long press
|
||||
/// gesture was detected.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [GestureDetector.onLongPressUp], which matches this signature.
|
||||
typedef GestureLongPressUpCallback = void Function();
|
||||
|
||||
/// Callback signature for [LongPressGestureRecognizer.onLongPressStart].
|
||||
|
@ -26,6 +60,12 @@ typedef GestureLongPressUpCallback = void Function();
|
|||
/// Called when a pointer has remained in contact with the screen at the
|
||||
/// same location for a long period of time. Also reports the long press down
|
||||
/// position.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [GestureDetector.onLongPressStart], which matches this signature.
|
||||
/// * [GestureLongPressCallback], which is the same signature without the
|
||||
/// details.
|
||||
typedef GestureLongPressStartCallback = void Function(LongPressStartDetails details);
|
||||
|
||||
/// Callback signature for [LongPressGestureRecognizer.onLongPressMoveUpdate].
|
||||
|
@ -33,6 +73,10 @@ typedef GestureLongPressStartCallback = void Function(LongPressStartDetails deta
|
|||
/// Called when a pointer is moving after being held in contact at the same
|
||||
/// location for a long period of time. Reports the new position and its offset
|
||||
/// from the original down position.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [GestureDetector.onLongPressMoveUpdate], which matches this signature.
|
||||
typedef GestureLongPressMoveUpdateCallback = void Function(LongPressMoveUpdateDetails details);
|
||||
|
||||
/// Callback signature for [LongPressGestureRecognizer.onLongPressEnd].
|
||||
|
@ -40,8 +84,46 @@ typedef GestureLongPressMoveUpdateCallback = void Function(LongPressMoveUpdateDe
|
|||
/// Called when a pointer stops contacting the screen after a long press
|
||||
/// gesture was detected. Also reports the position where the pointer stopped
|
||||
/// contacting the screen.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [GestureDetector.onLongPressEnd], which matches this signature.
|
||||
typedef GestureLongPressEndCallback = void Function(LongPressEndDetails details);
|
||||
|
||||
/// Details for callbacks that use [GestureLongPressDownCallback].
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [LongPressGestureRecognizer.onLongPressDown], whose callback passes
|
||||
/// these details.
|
||||
/// * [LongPressGestureRecognizer.onSecondaryLongPressDown], whose callback
|
||||
/// passes these details.
|
||||
/// * [LongPressGestureRecognizer.onTertiaryLongPressDown], whose callback
|
||||
/// passes these details.
|
||||
class LongPressDownDetails {
|
||||
/// Creates the details for a [GestureLongPressDownCallback].
|
||||
///
|
||||
/// The `globalPosition` argument must not be null.
|
||||
///
|
||||
/// If the `localPosition` argument is not specified, it will default to the
|
||||
/// global position.
|
||||
const LongPressDownDetails({
|
||||
this.globalPosition = Offset.zero,
|
||||
Offset? localPosition,
|
||||
this.kind,
|
||||
}) : assert(globalPosition != null),
|
||||
localPosition = localPosition ?? globalPosition;
|
||||
|
||||
/// The global position at which the pointer contacted the screen.
|
||||
final Offset globalPosition;
|
||||
|
||||
/// The kind of the device that initiated the event.
|
||||
final PointerDeviceKind? kind;
|
||||
|
||||
/// The local position at which the pointer contacted the screen.
|
||||
final Offset localPosition;
|
||||
}
|
||||
|
||||
/// Details for callbacks that use [GestureLongPressStartCallback].
|
||||
///
|
||||
/// See also:
|
||||
|
@ -59,10 +141,10 @@ class LongPressStartDetails {
|
|||
}) : assert(globalPosition != null),
|
||||
localPosition = localPosition ?? globalPosition;
|
||||
|
||||
/// The global position at which the pointer contacted the screen.
|
||||
/// The global position at which the pointer initially contacted the screen.
|
||||
final Offset globalPosition;
|
||||
|
||||
/// The local position at which the pointer contacted the screen.
|
||||
/// The local position at which the pointer initially contacted the screen.
|
||||
final Offset localPosition;
|
||||
}
|
||||
|
||||
|
@ -178,21 +260,63 @@ class LongPressGestureRecognizer extends PrimaryPointerGestureRecognizer {
|
|||
// different set of buttons, the gesture is canceled.
|
||||
int? _initialButtons;
|
||||
|
||||
/// Called when a long press gesture by a primary button has been recognized.
|
||||
/// Called when a pointer has contacted the screen at a particular location
|
||||
/// with a primary button, which might be the start of a long-press.
|
||||
///
|
||||
/// This triggers after the pointer down event.
|
||||
///
|
||||
/// If this recognizer doesn't win the arena, [onLongPressCancel] is called
|
||||
/// next. Otherwise, [onLongPressStart] is called next.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [kPrimaryButton], the button this callback responds to.
|
||||
/// * [onSecondaryLongPressDown], a similar callback but for a secondary button.
|
||||
/// * [onTertiaryLongPressDown], a similar callback but for a tertiary button.
|
||||
/// * [LongPressDownDetails], which is passed as an argument to this callback.
|
||||
/// * [GestureDetector.onLongPressDown], which exposes this callback in a widget.
|
||||
GestureLongPressDownCallback? onLongPressDown;
|
||||
|
||||
/// Called when a pointer that previously triggered [onLongPressDown] will
|
||||
/// not end up causing a long-press.
|
||||
///
|
||||
/// This triggers once the gesture loses the arena if [onLongPressDown] has
|
||||
/// previously been triggered.
|
||||
///
|
||||
/// If this recognizer wins the arena, [onLongPressStart] and [onLongPress]
|
||||
/// are called instead.
|
||||
///
|
||||
/// If the gesture is deactivated due to [postAcceptSlopTolerance] having
|
||||
/// been exceeded, this callback will not be called, since the gesture will
|
||||
/// have already won the arena at that point.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [kPrimaryButton], the button this callback responds to.
|
||||
GestureLongPressCancelCallback? onLongPressCancel;
|
||||
|
||||
/// Called when a long press gesture by a primary button has been recognized.
|
||||
///
|
||||
/// This is equivalent to (and is called immediately after) [onLongPressStart].
|
||||
/// The only difference between the two is that this callback does not
|
||||
/// contain details of the position at which the pointer initially contacted
|
||||
/// the screen.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [kPrimaryButton], the button this callback responds to.
|
||||
/// * [onLongPressStart], which has the same timing but has data for the
|
||||
/// press location.
|
||||
GestureLongPressCallback? onLongPress;
|
||||
|
||||
/// Called when a long press gesture by a primary button has been recognized.
|
||||
///
|
||||
/// This is equivalent to (and is called immediately before) [onLongPress].
|
||||
/// The only difference between the two is that this callback contains
|
||||
/// details of the position at which the pointer initially contacted the
|
||||
/// screen, whereas [onLongPress] does not.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [kPrimaryButton], the button this callback responds to.
|
||||
/// * [onLongPress], which has the same timing but without details.
|
||||
/// * [LongPressStartDetails], which is passed as an argument to this callback.
|
||||
GestureLongPressStartCallback? onLongPressStart;
|
||||
|
||||
|
@ -208,40 +332,90 @@ class LongPressGestureRecognizer extends PrimaryPointerGestureRecognizer {
|
|||
/// Called when the pointer stops contacting the screen after a long-press
|
||||
/// by a primary button.
|
||||
///
|
||||
/// This is equivalent to (and is called immediately after) [onLongPressEnd].
|
||||
/// The only difference between the two is that this callback does not
|
||||
/// contain details of the state of the pointer when it stopped contacting
|
||||
/// the screen.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [kPrimaryButton], the button this callback responds to.
|
||||
/// * [onLongPressEnd], which has the same timing but has data for the up
|
||||
/// gesture location.
|
||||
GestureLongPressUpCallback? onLongPressUp;
|
||||
|
||||
/// Called when the pointer stops contacting the screen after a long-press
|
||||
/// by a primary button.
|
||||
///
|
||||
/// This is equivalent to (and is called immediately before) [onLongPressUp].
|
||||
/// The only difference between the two is that this callback contains
|
||||
/// details of the state of the pointer when it stopped contacting the
|
||||
/// screen, whereas [onLongPressUp] does not.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [kPrimaryButton], the button this callback responds to.
|
||||
/// * [onLongPressUp], which has the same timing, but without details.
|
||||
/// * [LongPressEndDetails], which is passed as an argument to this
|
||||
/// callback.
|
||||
GestureLongPressEndCallback? onLongPressEnd;
|
||||
|
||||
/// Called when a long press gesture by a secondary button has been
|
||||
/// recognized.
|
||||
/// Called when a pointer has contacted the screen at a particular location
|
||||
/// with a secondary button, which might be the start of a long-press.
|
||||
///
|
||||
/// This triggers after the pointer down event.
|
||||
///
|
||||
/// If this recognizer doesn't win the arena, [onSecondaryLongPressCancel] is
|
||||
/// called next. Otherwise, [onSecondaryLongPressStart] is called next.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [kSecondaryButton], the button this callback responds to.
|
||||
/// * [onLongPressDown], a similar callback but for a primary button.
|
||||
/// * [onTertiaryLongPressDown], a similar callback but for a tertiary button.
|
||||
/// * [LongPressDownDetails], which is passed as an argument to this callback.
|
||||
/// * [GestureDetector.onSecondaryLongPressDown], which exposes this callback
|
||||
/// in a widget.
|
||||
GestureLongPressDownCallback? onSecondaryLongPressDown;
|
||||
|
||||
/// Called when a pointer that previously triggered [onSecondaryLongPressDown]
|
||||
/// will not end up causing a long-press.
|
||||
///
|
||||
/// This triggers once the gesture loses the arena if
|
||||
/// [onSecondaryLongPressDown] has previously been triggered.
|
||||
///
|
||||
/// If this recognizer wins the arena, [onSecondaryLongPressStart] and
|
||||
/// [onSecondaryLongPress] are called instead.
|
||||
///
|
||||
/// If the gesture is deactivated due to [postAcceptSlopTolerance] having
|
||||
/// been exceeded, this callback will not be called, since the gesture will
|
||||
/// have already won the arena at that point.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [kSecondaryButton], the button this callback responds to.
|
||||
GestureLongPressCancelCallback? onSecondaryLongPressCancel;
|
||||
|
||||
/// Called when a long press gesture by a secondary button has been
|
||||
/// recognized.
|
||||
///
|
||||
/// This is equivalent to (and is called immediately after)
|
||||
/// [onSecondaryLongPressStart]. The only difference between the two is that
|
||||
/// this callback does not contain details of the position at which the
|
||||
/// pointer initially contacted the screen.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [kSecondaryButton], the button this callback responds to.
|
||||
/// * [onSecondaryLongPressStart], which has the same timing but has data for
|
||||
/// the press location.
|
||||
GestureLongPressCallback? onSecondaryLongPress;
|
||||
|
||||
/// Called when a long press gesture by a secondary button has been recognized.
|
||||
///
|
||||
/// This is equivalent to (and is called immediately before)
|
||||
/// [onSecondaryLongPress]. The only difference between the two is that this
|
||||
/// callback contains details of the position at which the pointer initially
|
||||
/// contacted the screen, whereas [onSecondaryLongPress] does not.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [kSecondaryButton], the button this callback responds to.
|
||||
/// * [onSecondaryLongPress], which has the same timing but without details.
|
||||
/// * [LongPressStartDetails], which is passed as an argument to this
|
||||
/// callback.
|
||||
GestureLongPressStartCallback? onSecondaryLongPressStart;
|
||||
|
@ -259,40 +433,89 @@ class LongPressGestureRecognizer extends PrimaryPointerGestureRecognizer {
|
|||
/// Called when the pointer stops contacting the screen after a long-press by
|
||||
/// a secondary button.
|
||||
///
|
||||
/// This is equivalent to (and is called immediately after)
|
||||
/// [onSecondaryLongPressEnd]. The only difference between the two is that
|
||||
/// this callback does not contain details of the state of the pointer when
|
||||
/// it stopped contacting the screen.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [kSecondaryButton], the button this callback responds to.
|
||||
/// * [onSecondaryLongPressEnd], which has the same timing but has data for
|
||||
/// the up gesture location.
|
||||
GestureLongPressUpCallback? onSecondaryLongPressUp;
|
||||
|
||||
/// Called when the pointer stops contacting the screen after a long-press by
|
||||
/// a secondary button.
|
||||
///
|
||||
/// This is equivalent to (and is called immediately before)
|
||||
/// [onSecondaryLongPressUp]. The only difference between the two is that
|
||||
/// this callback contains details of the state of the pointer when it
|
||||
/// stopped contacting the screen, whereas [onSecondaryLongPressUp] does not.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [kSecondaryButton], the button this callback responds to.
|
||||
/// * [onSecondaryLongPressUp], which has the same timing, but without
|
||||
/// details.
|
||||
/// * [LongPressEndDetails], which is passed as an argument to this callback.
|
||||
GestureLongPressEndCallback? onSecondaryLongPressEnd;
|
||||
|
||||
/// Called when a pointer has contacted the screen at a particular location
|
||||
/// with a tertiary button, which might be the start of a long-press.
|
||||
///
|
||||
/// This triggers after the pointer down event.
|
||||
///
|
||||
/// If this recognizer doesn't win the arena, [onTertiaryLongPressCancel] is
|
||||
/// called next. Otherwise, [onTertiaryLongPressStart] is called next.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [kTertiaryButton], the button this callback responds to.
|
||||
/// * [onLongPressDown], a similar callback but for a primary button.
|
||||
/// * [onSecondaryLongPressDown], a similar callback but for a secondary button.
|
||||
/// * [LongPressDownDetails], which is passed as an argument to this callback.
|
||||
/// * [GestureDetector.onTertiaryLongPressDown], which exposes this callback
|
||||
/// in a widget.
|
||||
GestureLongPressDownCallback? onTertiaryLongPressDown;
|
||||
|
||||
/// Called when a pointer that previously triggered [onTertiaryLongPressDown]
|
||||
/// will not end up causing a long-press.
|
||||
///
|
||||
/// This triggers once the gesture loses the arena if
|
||||
/// [onTertiaryLongPressDown] has previously been triggered.
|
||||
///
|
||||
/// If this recognizer wins the arena, [onTertiaryLongPressStart] and
|
||||
/// [onTertiaryLongPress] are called instead.
|
||||
///
|
||||
/// If the gesture is deactivated due to [postAcceptSlopTolerance] having
|
||||
/// been exceeded, this callback will not be called, since the gesture will
|
||||
/// have already won the arena at that point.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [kTertiaryButton], the button this callback responds to.
|
||||
GestureLongPressCancelCallback? onTertiaryLongPressCancel;
|
||||
|
||||
/// Called when a long press gesture by a tertiary button has been
|
||||
/// recognized.
|
||||
///
|
||||
/// This is equivalent to (and is called immediately after)
|
||||
/// [onTertiaryLongPressStart]. The only difference between the two is that
|
||||
/// this callback does not contain details of the position at which the
|
||||
/// pointer initially contacted the screen.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [kTertiaryButton], the button this callback responds to.
|
||||
/// * [onTertiaryLongPressStart], which has the same timing but has data for
|
||||
/// the press location.
|
||||
GestureLongPressCallback? onTertiaryLongPress;
|
||||
|
||||
/// Called when a long press gesture by a tertiary button has been recognized.
|
||||
///
|
||||
/// This is equivalent to (and is called immediately before)
|
||||
/// [onTertiaryLongPress]. The only difference between the two is that this
|
||||
/// callback contains details of the position at which the pointer initially
|
||||
/// contacted the screen, whereas [onTertiaryLongPress] does not.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [kTertiaryButton], the button this callback responds to.
|
||||
/// * [onTertiaryLongPress], which has the same timing but without details.
|
||||
/// * [LongPressStartDetails], which is passed as an argument to this
|
||||
/// callback.
|
||||
GestureLongPressStartCallback? onTertiaryLongPressStart;
|
||||
|
@ -310,21 +533,27 @@ class LongPressGestureRecognizer extends PrimaryPointerGestureRecognizer {
|
|||
/// Called when the pointer stops contacting the screen after a long-press by
|
||||
/// a tertiary button.
|
||||
///
|
||||
/// This is equivalent to (and is called immediately after)
|
||||
/// [onTertiaryLongPressEnd]. The only difference between the two is that
|
||||
/// this callback does not contain details of the state of the pointer when
|
||||
/// it stopped contacting the screen.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [kTertiaryButton], the button this callback responds to.
|
||||
/// * [onTertiaryLongPressEnd], which has the same timing but has data for
|
||||
/// the up gesture location.
|
||||
GestureLongPressUpCallback? onTertiaryLongPressUp;
|
||||
|
||||
/// Called when the pointer stops contacting the screen after a long-press by
|
||||
/// a tertiary button.
|
||||
///
|
||||
/// This is equivalent to (and is called immediately before)
|
||||
/// [onTertiaryLongPressUp]. The only difference between the two is that
|
||||
/// this callback contains details of the state of the pointer when it
|
||||
/// stopped contacting the screen, whereas [onTertiaryLongPressUp] does not.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [kTertiaryButton], the button this callback responds to.
|
||||
/// * [onTertiaryLongPressUp], which has the same timing, but without
|
||||
/// details.
|
||||
/// * [LongPressEndDetails], which is passed as an argument to this callback.
|
||||
GestureLongPressEndCallback? onTertiaryLongPressEnd;
|
||||
|
||||
|
@ -334,7 +563,9 @@ class LongPressGestureRecognizer extends PrimaryPointerGestureRecognizer {
|
|||
bool isPointerAllowed(PointerDownEvent event) {
|
||||
switch (event.buttons) {
|
||||
case kPrimaryButton:
|
||||
if (onLongPressStart == null &&
|
||||
if (onLongPressDown == null &&
|
||||
onLongPressCancel == null &&
|
||||
onLongPressStart == null &&
|
||||
onLongPress == null &&
|
||||
onLongPressMoveUpdate == null &&
|
||||
onLongPressEnd == null &&
|
||||
|
@ -342,7 +573,9 @@ class LongPressGestureRecognizer extends PrimaryPointerGestureRecognizer {
|
|||
return false;
|
||||
break;
|
||||
case kSecondaryButton:
|
||||
if (onSecondaryLongPressStart == null &&
|
||||
if (onSecondaryLongPressDown == null &&
|
||||
onSecondaryLongPressCancel == null &&
|
||||
onSecondaryLongPressStart == null &&
|
||||
onSecondaryLongPress == null &&
|
||||
onSecondaryLongPressMoveUpdate == null &&
|
||||
onSecondaryLongPressEnd == null &&
|
||||
|
@ -350,7 +583,9 @@ class LongPressGestureRecognizer extends PrimaryPointerGestureRecognizer {
|
|||
return false;
|
||||
break;
|
||||
case kTertiaryButton:
|
||||
if (onTertiaryLongPressStart == null &&
|
||||
if (onTertiaryLongPressDown == null &&
|
||||
onTertiaryLongPressCancel == null &&
|
||||
onTertiaryLongPressStart == null &&
|
||||
onTertiaryLongPress == null &&
|
||||
onTertiaryLongPressMoveUpdate == null &&
|
||||
onTertiaryLongPressEnd == null &&
|
||||
|
@ -394,11 +629,13 @@ class LongPressGestureRecognizer extends PrimaryPointerGestureRecognizer {
|
|||
}
|
||||
_reset();
|
||||
} else if (event is PointerCancelEvent) {
|
||||
_checkLongPressCancel();
|
||||
_reset();
|
||||
} else if (event is PointerDownEvent) {
|
||||
// The first touch.
|
||||
_longPressOrigin = OffsetPair.fromEventPosition(event);
|
||||
_initialButtons = event.buttons;
|
||||
_checkLongPressDown(event);
|
||||
} else if (event is PointerMoveEvent) {
|
||||
if (event.buttons != _initialButtons) {
|
||||
resolve(GestureDisposition.rejected);
|
||||
|
@ -409,6 +646,52 @@ class LongPressGestureRecognizer extends PrimaryPointerGestureRecognizer {
|
|||
}
|
||||
}
|
||||
|
||||
void _checkLongPressDown(PointerDownEvent event) {
|
||||
assert(_longPressOrigin != null);
|
||||
final LongPressDownDetails details = LongPressDownDetails(
|
||||
globalPosition: _longPressOrigin!.global,
|
||||
localPosition: _longPressOrigin!.local,
|
||||
kind: getKindForPointer(event.pointer),
|
||||
);
|
||||
switch (_initialButtons) {
|
||||
case kPrimaryButton:
|
||||
if (onLongPressDown != null)
|
||||
invokeCallback<void>('onLongPressDown', () => onLongPressDown!(details));
|
||||
break;
|
||||
case kSecondaryButton:
|
||||
if (onSecondaryLongPressDown != null)
|
||||
invokeCallback<void>('onSecondaryLongPressDown', () => onSecondaryLongPressDown!(details));
|
||||
break;
|
||||
case kTertiaryButton:
|
||||
if (onTertiaryLongPressDown != null)
|
||||
invokeCallback<void>('onTertiaryLongPressDown', () => onTertiaryLongPressDown!(details));
|
||||
break;
|
||||
default:
|
||||
assert(false, 'Unhandled button $_initialButtons');
|
||||
}
|
||||
}
|
||||
|
||||
void _checkLongPressCancel() {
|
||||
if (state == GestureRecognizerState.possible) {
|
||||
switch (_initialButtons) {
|
||||
case kPrimaryButton:
|
||||
if (onLongPressCancel != null)
|
||||
invokeCallback<void>('onLongPressCancel', onLongPressCancel!);
|
||||
break;
|
||||
case kSecondaryButton:
|
||||
if (onSecondaryLongPressCancel != null)
|
||||
invokeCallback<void>('onSecondaryLongPressCancel', onSecondaryLongPressCancel!);
|
||||
break;
|
||||
case kTertiaryButton:
|
||||
if (onTertiaryLongPressCancel != null)
|
||||
invokeCallback<void>('onTertiaryLongPressCancel', onTertiaryLongPressCancel!);
|
||||
break;
|
||||
default:
|
||||
assert(false, 'Unhandled button $_initialButtons');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _checkLongPressStart() {
|
||||
switch (_initialButtons) {
|
||||
case kPrimaryButton:
|
||||
|
@ -531,10 +814,14 @@ class LongPressGestureRecognizer extends PrimaryPointerGestureRecognizer {
|
|||
|
||||
@override
|
||||
void resolve(GestureDisposition disposition) {
|
||||
if (_longPressAccepted && disposition == GestureDisposition.rejected) {
|
||||
// This can happen if the gesture has been canceled. For example when
|
||||
// the buttons have changed.
|
||||
_reset();
|
||||
if (disposition == GestureDisposition.rejected) {
|
||||
if (_longPressAccepted) {
|
||||
// This can happen if the gesture has been canceled. For example when
|
||||
// the buttons have changed.
|
||||
_reset();
|
||||
} else {
|
||||
_checkLongPressCancel();
|
||||
}
|
||||
}
|
||||
super.resolve(disposition);
|
||||
}
|
||||
|
|
|
@ -394,6 +394,7 @@ class TapGestureRecognizer extends BaseTapGestureRecognizer {
|
|||
/// See also:
|
||||
///
|
||||
/// * [kPrimaryButton], the button this callback responds to.
|
||||
/// * [onSecondaryTap], a similar callback but for a secondary button.
|
||||
/// * [onTapUp], which has the same timing but with details.
|
||||
/// * [GestureDetector.onTap], which exposes this callback.
|
||||
GestureTapCallback? onTap;
|
||||
|
|
|
@ -230,16 +230,27 @@ class GestureDetector extends StatelessWidget {
|
|||
this.onDoubleTapDown,
|
||||
this.onDoubleTap,
|
||||
this.onDoubleTapCancel,
|
||||
this.onLongPressDown,
|
||||
this.onLongPressCancel,
|
||||
this.onLongPress,
|
||||
this.onLongPressStart,
|
||||
this.onLongPressMoveUpdate,
|
||||
this.onLongPressUp,
|
||||
this.onLongPressEnd,
|
||||
this.onSecondaryLongPressDown,
|
||||
this.onSecondaryLongPressCancel,
|
||||
this.onSecondaryLongPress,
|
||||
this.onSecondaryLongPressStart,
|
||||
this.onSecondaryLongPressMoveUpdate,
|
||||
this.onSecondaryLongPressUp,
|
||||
this.onSecondaryLongPressEnd,
|
||||
this.onTertiaryLongPressDown,
|
||||
this.onTertiaryLongPressCancel,
|
||||
this.onTertiaryLongPress,
|
||||
this.onTertiaryLongPressStart,
|
||||
this.onTertiaryLongPressMoveUpdate,
|
||||
this.onTertiaryLongPressUp,
|
||||
this.onTertiaryLongPressEnd,
|
||||
this.onVerticalDragDown,
|
||||
this.onVerticalDragStart,
|
||||
this.onVerticalDragUpdate,
|
||||
|
@ -458,15 +469,55 @@ class GestureDetector extends StatelessWidget {
|
|||
/// * [kPrimaryButton], the button this callback responds to.
|
||||
final GestureTapCancelCallback? onDoubleTapCancel;
|
||||
|
||||
/// The pointer has contacted the screen with a primary button, which might
|
||||
/// be the start of a long-press.
|
||||
///
|
||||
/// This triggers after the pointer down event.
|
||||
///
|
||||
/// If the user completes the long-press, and this gesture wins,
|
||||
/// [onLongPressStart] will be called after this callback. Otherwise,
|
||||
/// [onLongPressCancel] will be called after this callback.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [kPrimaryButton], the button this callback responds to.
|
||||
/// * [onSecondaryLongPressDown], a similar callback but for a secondary button.
|
||||
/// * [onTertiaryLongPressDown], a similar callback but for a tertiary button.
|
||||
/// * [LongPressGestureRecognizer.onLongPressDown], which exposes this
|
||||
/// callback at the gesture layer.
|
||||
final GestureLongPressDownCallback? onLongPressDown;
|
||||
|
||||
/// A pointer that previously triggered [onLongPressDown] will not end up
|
||||
/// causing a long-press.
|
||||
///
|
||||
/// This triggers once the gesture loses if [onLongPressDown] has previously
|
||||
/// been triggered.
|
||||
///
|
||||
/// If the user completed the long-press, and the gesture won, then
|
||||
/// [onLongPressStart] and [onLongPress] are called instead.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [kPrimaryButton], the button this callback responds to.
|
||||
/// * [LongPressGestureRecognizer.onLongPressCancel], which exposes this
|
||||
/// callback at the gesture layer.
|
||||
final GestureLongPressCancelCallback? onLongPressCancel;
|
||||
|
||||
/// Called when a long press gesture with a primary button has been recognized.
|
||||
///
|
||||
/// Triggered when a pointer has remained in contact with the screen at the
|
||||
/// same location for a long period of time.
|
||||
///
|
||||
/// This is equivalent to (and is called immediately after) [onLongPressStart].
|
||||
/// The only difference between the two is that this callback does not
|
||||
/// contain details of the position at which the pointer initially contacted
|
||||
/// the screen.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [kPrimaryButton], the button this callback responds to.
|
||||
/// * [onLongPressStart], which has the same timing but has gesture details.
|
||||
/// * [LongPressGestureRecognizer.onLongPress], which exposes this
|
||||
/// callback at the gesture layer.
|
||||
final GestureLongPressCallback? onLongPress;
|
||||
|
||||
/// Called when a long press gesture with a primary button has been recognized.
|
||||
|
@ -474,49 +525,107 @@ class GestureDetector extends StatelessWidget {
|
|||
/// Triggered when a pointer has remained in contact with the screen at the
|
||||
/// same location for a long period of time.
|
||||
///
|
||||
/// This is equivalent to (and is called immediately before) [onLongPress].
|
||||
/// The only difference between the two is that this callback contains
|
||||
/// details of the position at which the pointer initially contacted the
|
||||
/// screen, whereas [onLongPress] does not.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [kPrimaryButton], the button this callback responds to.
|
||||
/// * [onLongPress], which has the same timing but without the gesture details.
|
||||
/// * [LongPressGestureRecognizer.onLongPressStart], which exposes this
|
||||
/// callback at the gesture layer.
|
||||
final GestureLongPressStartCallback? onLongPressStart;
|
||||
|
||||
/// A pointer has been drag-moved after a long press with a primary button.
|
||||
/// A pointer has been drag-moved after a long-press with a primary button.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [kPrimaryButton], the button this callback responds to.
|
||||
/// * [LongPressGestureRecognizer.onLongPressMoveUpdate], which exposes this
|
||||
/// callback at the gesture layer.
|
||||
final GestureLongPressMoveUpdateCallback? onLongPressMoveUpdate;
|
||||
|
||||
/// A pointer that has triggered a long-press with a primary button has
|
||||
/// stopped contacting the screen.
|
||||
///
|
||||
/// This is equivalent to (and is called immediately after) [onLongPressEnd].
|
||||
/// The only difference between the two is that this callback does not
|
||||
/// contain details of the state of the pointer when it stopped contacting
|
||||
/// the screen.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [kPrimaryButton], the button this callback responds to.
|
||||
/// * [onLongPressEnd], which has the same timing but has gesture details.
|
||||
/// * [LongPressGestureRecognizer.onLongPressUp], which exposes this
|
||||
/// callback at the gesture layer.
|
||||
final GestureLongPressUpCallback? onLongPressUp;
|
||||
|
||||
/// A pointer that has triggered a long-press with a primary button has
|
||||
/// stopped contacting the screen.
|
||||
///
|
||||
/// This is equivalent to (and is called immediately before) [onLongPressUp].
|
||||
/// The only difference between the two is that this callback contains
|
||||
/// details of the state of the pointer when it stopped contacting the
|
||||
/// screen, whereas [onLongPressUp] does not.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [kPrimaryButton], the button this callback responds to.
|
||||
/// * [onLongPressUp], which has the same timing but without the gesture
|
||||
/// details.
|
||||
/// * [LongPressGestureRecognizer.onLongPressEnd], which exposes this
|
||||
/// callback at the gesture layer.
|
||||
final GestureLongPressEndCallback? onLongPressEnd;
|
||||
|
||||
/// The pointer has contacted the screen with a secondary button, which might
|
||||
/// be the start of a long-press.
|
||||
///
|
||||
/// This triggers after the pointer down event.
|
||||
///
|
||||
/// If the user completes the long-press, and this gesture wins,
|
||||
/// [onSecondaryLongPressStart] will be called after this callback. Otherwise,
|
||||
/// [onSecondaryLongPressCancel] will be called after this callback.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [kSecondaryButton], the button this callback responds to.
|
||||
/// * [onLongPressDown], a similar callback but for a secondary button.
|
||||
/// * [onTertiaryLongPressDown], a similar callback but for a tertiary button.
|
||||
/// * [LongPressGestureRecognizer.onSecondaryLongPressDown], which exposes
|
||||
/// this callback at the gesture layer.
|
||||
final GestureLongPressDownCallback? onSecondaryLongPressDown;
|
||||
|
||||
/// A pointer that previously triggered [onSecondaryLongPressDown] will not
|
||||
/// end up causing a long-press.
|
||||
///
|
||||
/// This triggers once the gesture loses if [onSecondaryLongPressDown] has
|
||||
/// previously been triggered.
|
||||
///
|
||||
/// If the user completed the long-press, and the gesture won, then
|
||||
/// [onSecondaryLongPressStart] and [onSecondaryLongPress] are called instead.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [kSecondaryButton], the button this callback responds to.
|
||||
/// * [LongPressGestureRecognizer.onSecondaryLongPressCancel], which exposes
|
||||
/// this callback at the gesture layer.
|
||||
final GestureLongPressCancelCallback? onSecondaryLongPressCancel;
|
||||
|
||||
/// Called when a long press gesture with a secondary button has been
|
||||
/// recognized.
|
||||
///
|
||||
/// Triggered when a pointer has remained in contact with the screen at the
|
||||
/// same location for a long period of time.
|
||||
///
|
||||
/// This is equivalent to (and is called immediately after)
|
||||
/// [onSecondaryLongPressStart]. The only difference between the two is that
|
||||
/// this callback does not contain details of the position at which the
|
||||
/// pointer initially contacted the screen.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [kSecondaryButton], the button this callback responds to.
|
||||
/// * [onSecondaryLongPressStart], which has the same timing but has gesture
|
||||
/// details.
|
||||
/// * [LongPressGestureRecognizer.onSecondaryLongPress], which exposes
|
||||
/// this callback at the gesture layer.
|
||||
final GestureLongPressCallback? onSecondaryLongPress;
|
||||
|
||||
/// Called when a long press gesture with a secondary button has been
|
||||
|
@ -525,11 +634,16 @@ class GestureDetector extends StatelessWidget {
|
|||
/// Triggered when a pointer has remained in contact with the screen at the
|
||||
/// same location for a long period of time.
|
||||
///
|
||||
/// This is equivalent to (and is called immediately before)
|
||||
/// [onSecondaryLongPress]. The only difference between the two is that this
|
||||
/// callback contains details of the position at which the pointer initially
|
||||
/// contacted the screen, whereas [onSecondaryLongPress] does not.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [kSecondaryButton], the button this callback responds to.
|
||||
/// * [onSecondaryLongPress], which has the same timing but without the
|
||||
/// gesture details.
|
||||
/// * [LongPressGestureRecognizer.onSecondaryLongPressStart], which exposes
|
||||
/// this callback at the gesture layer.
|
||||
final GestureLongPressStartCallback? onSecondaryLongPressStart;
|
||||
|
||||
/// A pointer has been drag-moved after a long press with a secondary button.
|
||||
|
@ -537,28 +651,149 @@ class GestureDetector extends StatelessWidget {
|
|||
/// See also:
|
||||
///
|
||||
/// * [kSecondaryButton], the button this callback responds to.
|
||||
/// * [LongPressGestureRecognizer.onSecondaryLongPressMoveUpdate], which exposes
|
||||
/// this callback at the gesture layer.
|
||||
final GestureLongPressMoveUpdateCallback? onSecondaryLongPressMoveUpdate;
|
||||
|
||||
/// A pointer that has triggered a long-press with a secondary button has
|
||||
/// stopped contacting the screen.
|
||||
///
|
||||
/// This is equivalent to (and is called immediately after)
|
||||
/// [onSecondaryLongPressEnd]. The only difference between the two is that
|
||||
/// this callback does not contain details of the state of the pointer when
|
||||
/// it stopped contacting the screen.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [kSecondaryButton], the button this callback responds to.
|
||||
/// * [onSecondaryLongPressEnd], which has the same timing but has gesture
|
||||
/// details.
|
||||
/// * [LongPressGestureRecognizer.onSecondaryLongPressUp], which exposes
|
||||
/// this callback at the gesture layer.
|
||||
final GestureLongPressUpCallback? onSecondaryLongPressUp;
|
||||
|
||||
/// A pointer that has triggered a long-press with a secondary button has
|
||||
/// stopped contacting the screen.
|
||||
///
|
||||
/// This is equivalent to (and is called immediately before)
|
||||
/// [onSecondaryLongPressUp]. The only difference between the two is that
|
||||
/// this callback contains details of the state of the pointer when it
|
||||
/// stopped contacting the screen, whereas [onSecondaryLongPressUp] does not.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [kSecondaryButton], the button this callback responds to.
|
||||
/// * [onSecondaryLongPressUp], which has the same timing but without the
|
||||
/// gesture details.
|
||||
/// * [LongPressGestureRecognizer.onSecondaryLongPressEnd], which exposes
|
||||
/// this callback at the gesture layer.
|
||||
final GestureLongPressEndCallback? onSecondaryLongPressEnd;
|
||||
|
||||
/// The pointer has contacted the screen with a tertiary button, which might
|
||||
/// be the start of a long-press.
|
||||
///
|
||||
/// This triggers after the pointer down event.
|
||||
///
|
||||
/// If the user completes the long-press, and this gesture wins,
|
||||
/// [onTertiaryLongPressStart] will be called after this callback. Otherwise,
|
||||
/// [onTertiaryLongPressCancel] will be called after this callback.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [kTertiaryButton], the button this callback responds to.
|
||||
/// * [onLongPressDown], a similar callback but for a primary button.
|
||||
/// * [onSecondaryLongPressDown], a similar callback but for a secondary button.
|
||||
/// * [LongPressGestureRecognizer.onTertiaryLongPressDown], which exposes
|
||||
/// this callback at the gesture layer.
|
||||
final GestureLongPressDownCallback? onTertiaryLongPressDown;
|
||||
|
||||
/// A pointer that previously triggered [onTertiaryLongPressDown] will not
|
||||
/// end up causing a long-press.
|
||||
///
|
||||
/// This triggers once the gesture loses if [onTertiaryLongPressDown] has
|
||||
/// previously been triggered.
|
||||
///
|
||||
/// If the user completed the long-press, and the gesture won, then
|
||||
/// [onTertiaryLongPressStart] and [onTertiaryLongPress] are called instead.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [kTertiaryButton], the button this callback responds to.
|
||||
/// * [LongPressGestureRecognizer.onTertiaryLongPressCancel], which exposes
|
||||
/// this callback at the gesture layer.
|
||||
final GestureLongPressCancelCallback? onTertiaryLongPressCancel;
|
||||
|
||||
/// Called when a long press gesture with a tertiary button has been
|
||||
/// recognized.
|
||||
///
|
||||
/// Triggered when a pointer has remained in contact with the screen at the
|
||||
/// same location for a long period of time.
|
||||
///
|
||||
/// This is equivalent to (and is called immediately after)
|
||||
/// [onTertiaryLongPressStart]. The only difference between the two is that
|
||||
/// this callback does not contain details of the position at which the
|
||||
/// pointer initially contacted the screen.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [kTertiaryButton], the button this callback responds to.
|
||||
/// * [LongPressGestureRecognizer.onTertiaryLongPress], which exposes
|
||||
/// this callback at the gesture layer.
|
||||
final GestureLongPressCallback? onTertiaryLongPress;
|
||||
|
||||
/// Called when a long press gesture with a tertiary button has been
|
||||
/// recognized.
|
||||
///
|
||||
/// Triggered when a pointer has remained in contact with the screen at the
|
||||
/// same location for a long period of time.
|
||||
///
|
||||
/// This is equivalent to (and is called immediately before)
|
||||
/// [onTertiaryLongPress]. The only difference between the two is that this
|
||||
/// callback contains details of the position at which the pointer initially
|
||||
/// contacted the screen, whereas [onTertiaryLongPress] does not.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [kTertiaryButton], the button this callback responds to.
|
||||
/// * [LongPressGestureRecognizer.onTertiaryLongPressStart], which exposes
|
||||
/// this callback at the gesture layer.
|
||||
final GestureLongPressStartCallback? onTertiaryLongPressStart;
|
||||
|
||||
/// A pointer has been drag-moved after a long press with a tertiary button.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [kTertiaryButton], the button this callback responds to.
|
||||
/// * [LongPressGestureRecognizer.onTertiaryLongPressMoveUpdate], which exposes
|
||||
/// this callback at the gesture layer.
|
||||
final GestureLongPressMoveUpdateCallback? onTertiaryLongPressMoveUpdate;
|
||||
|
||||
/// A pointer that has triggered a long-press with a tertiary button has
|
||||
/// stopped contacting the screen.
|
||||
///
|
||||
/// This is equivalent to (and is called immediately after)
|
||||
/// [onTertiaryLongPressEnd]. The only difference between the two is that
|
||||
/// this callback does not contain details of the state of the pointer when
|
||||
/// it stopped contacting the screen.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [kTertiaryButton], the button this callback responds to.
|
||||
/// * [LongPressGestureRecognizer.onTertiaryLongPressUp], which exposes
|
||||
/// this callback at the gesture layer.
|
||||
final GestureLongPressUpCallback? onTertiaryLongPressUp;
|
||||
|
||||
/// A pointer that has triggered a long-press with a tertiary button has
|
||||
/// stopped contacting the screen.
|
||||
///
|
||||
/// This is equivalent to (and is called immediately before)
|
||||
/// [onTertiaryLongPressUp]. The only difference between the two is that
|
||||
/// this callback contains details of the state of the pointer when it
|
||||
/// stopped contacting the screen, whereas [onTertiaryLongPressUp] does not.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [kTertiaryButton], the button this callback responds to.
|
||||
/// * [LongPressGestureRecognizer.onTertiaryLongPressEnd], which exposes
|
||||
/// this callback at the gesture layer.
|
||||
final GestureLongPressEndCallback? onTertiaryLongPressEnd;
|
||||
|
||||
/// A pointer has contacted the screen with a primary button and might begin
|
||||
/// to move vertically.
|
||||
///
|
||||
|
@ -804,30 +1039,52 @@ class GestureDetector extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
|
||||
if (onLongPress != null ||
|
||||
onLongPressUp != null ||
|
||||
if (onLongPressDown != null ||
|
||||
onLongPressCancel != null ||
|
||||
onLongPress != null ||
|
||||
onLongPressStart != null ||
|
||||
onLongPressMoveUpdate != null ||
|
||||
onLongPressUp != null ||
|
||||
onLongPressEnd != null ||
|
||||
onSecondaryLongPressDown != null ||
|
||||
onSecondaryLongPressCancel != null ||
|
||||
onSecondaryLongPress != null ||
|
||||
onSecondaryLongPressUp != null ||
|
||||
onSecondaryLongPressStart != null ||
|
||||
onSecondaryLongPressMoveUpdate != null ||
|
||||
onSecondaryLongPressEnd != null) {
|
||||
onSecondaryLongPressUp != null ||
|
||||
onSecondaryLongPressEnd != null ||
|
||||
onTertiaryLongPressDown != null ||
|
||||
onTertiaryLongPressCancel != null ||
|
||||
onTertiaryLongPress != null ||
|
||||
onTertiaryLongPressStart != null ||
|
||||
onTertiaryLongPressMoveUpdate != null ||
|
||||
onTertiaryLongPressUp != null ||
|
||||
onTertiaryLongPressEnd != null) {
|
||||
gestures[LongPressGestureRecognizer] = GestureRecognizerFactoryWithHandlers<LongPressGestureRecognizer>(
|
||||
() => LongPressGestureRecognizer(debugOwner: this),
|
||||
(LongPressGestureRecognizer instance) {
|
||||
instance
|
||||
..onLongPressDown = onLongPressDown
|
||||
..onLongPressCancel = onLongPressCancel
|
||||
..onLongPress = onLongPress
|
||||
..onLongPressStart = onLongPressStart
|
||||
..onLongPressMoveUpdate = onLongPressMoveUpdate
|
||||
..onLongPressEnd = onLongPressEnd
|
||||
..onLongPressUp = onLongPressUp
|
||||
..onLongPressEnd = onLongPressEnd
|
||||
..onSecondaryLongPressDown = onSecondaryLongPressDown
|
||||
..onSecondaryLongPressCancel = onSecondaryLongPressCancel
|
||||
..onSecondaryLongPress = onSecondaryLongPress
|
||||
..onSecondaryLongPressStart = onSecondaryLongPressStart
|
||||
..onSecondaryLongPressMoveUpdate = onSecondaryLongPressMoveUpdate
|
||||
..onSecondaryLongPressUp = onSecondaryLongPressUp
|
||||
..onSecondaryLongPressEnd = onSecondaryLongPressEnd
|
||||
..onSecondaryLongPressUp = onSecondaryLongPressUp;
|
||||
..onTertiaryLongPressDown = onTertiaryLongPressDown
|
||||
..onTertiaryLongPressCancel = onTertiaryLongPressCancel
|
||||
..onTertiaryLongPress = onTertiaryLongPress
|
||||
..onTertiaryLongPressStart = onTertiaryLongPressStart
|
||||
..onTertiaryLongPressMoveUpdate = onTertiaryLongPressMoveUpdate
|
||||
..onTertiaryLongPressUp = onTertiaryLongPressUp
|
||||
..onTertiaryLongPressEnd = onTertiaryLongPressEnd;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -1029,7 +1286,8 @@ class RawGestureDetector extends StatefulWidget {
|
|||
/// * During a semantic tap, it calls [TapGestureRecognizer]'s
|
||||
/// `onTapDown`, `onTapUp`, and `onTap`.
|
||||
/// * During a semantic long press, it calls [LongPressGestureRecognizer]'s
|
||||
/// `onLongPressStart`, `onLongPress`, `onLongPressEnd` and `onLongPressUp`.
|
||||
/// `onLongPressDown`, `onLongPressStart`, `onLongPress`, `onLongPressEnd`
|
||||
/// and `onLongPressUp`.
|
||||
/// * During a semantic horizontal drag, it calls [HorizontalDragGestureRecognizer]'s
|
||||
/// `onDown`, `onStart`, `onUpdate` and `onEnd`, then
|
||||
/// [PanGestureRecognizer]'s `onDown`, `onStart`, `onUpdate` and `onEnd`.
|
||||
|
@ -1344,6 +1602,7 @@ class _DefaultSemanticsGestureDelegate extends SemanticsGestureDelegate {
|
|||
return null;
|
||||
|
||||
return () {
|
||||
longPress.onLongPressDown?.call(const LongPressDownDetails());
|
||||
longPress.onLongPressStart?.call(const LongPressStartDetails());
|
||||
longPress.onLongPress?.call();
|
||||
longPress.onLongPressEnd?.call(const LongPressEndDetails());
|
||||
|
|
|
@ -62,344 +62,342 @@ void main() {
|
|||
setUp(ensureGestureBinding);
|
||||
|
||||
group('Long press', () {
|
||||
late LongPressGestureRecognizer longPress;
|
||||
late bool longPressDown;
|
||||
late bool longPressUp;
|
||||
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(() {
|
||||
longPress = LongPressGestureRecognizer();
|
||||
longPressDown = false;
|
||||
longPress.onLongPress = () {
|
||||
longPressDown = true;
|
||||
};
|
||||
longPressUp = false;
|
||||
longPress.onLongPressUp = () {
|
||||
longPressUp = true;
|
||||
};
|
||||
recognized = <String>[];
|
||||
gesture = LongPressGestureRecognizer();
|
||||
setUpHandlers();
|
||||
});
|
||||
|
||||
testGesture('Should recognize long press', (GestureTester tester) {
|
||||
longPress.addPointer(down);
|
||||
gesture.addPointer(down);
|
||||
tester.closeArena(5);
|
||||
expect(longPressDown, isFalse);
|
||||
expect(recognized, const <String>[]);
|
||||
tester.route(down);
|
||||
expect(longPressDown, isFalse);
|
||||
expect(recognized, const <String>['down']);
|
||||
tester.async.elapse(const Duration(milliseconds: 300));
|
||||
expect(longPressDown, isFalse);
|
||||
expect(recognized, const <String>['down']);
|
||||
tester.async.elapse(const Duration(milliseconds: 700));
|
||||
expect(longPressDown, isTrue);
|
||||
|
||||
longPress.dispose();
|
||||
expect(recognized, const <String>['down', 'start']);
|
||||
gesture.dispose();
|
||||
expect(recognized, const <String>['down', 'start']);
|
||||
});
|
||||
|
||||
testGesture('Should recognize long press with altered duration', (GestureTester tester) {
|
||||
longPress = LongPressGestureRecognizer(duration: const Duration(milliseconds: 100));
|
||||
longPressDown = false;
|
||||
longPress.onLongPress = () {
|
||||
longPressDown = true;
|
||||
};
|
||||
longPressUp = false;
|
||||
longPress.onLongPressUp = () {
|
||||
longPressUp = true;
|
||||
};
|
||||
longPress.addPointer(down);
|
||||
gesture = LongPressGestureRecognizer(duration: const Duration(milliseconds: 100));
|
||||
setUpHandlers();
|
||||
gesture.addPointer(down);
|
||||
tester.closeArena(5);
|
||||
expect(longPressDown, isFalse);
|
||||
expect(recognized, const <String>[]);
|
||||
tester.route(down);
|
||||
expect(longPressDown, isFalse);
|
||||
expect(recognized, const <String>['down']);
|
||||
tester.async.elapse(const Duration(milliseconds: 50));
|
||||
expect(longPressDown, isFalse);
|
||||
expect(recognized, const <String>['down']);
|
||||
tester.async.elapse(const Duration(milliseconds: 50));
|
||||
expect(longPressDown, isTrue);
|
||||
|
||||
longPress.dispose();
|
||||
expect(recognized, const <String>['down', 'start']);
|
||||
gesture.dispose();
|
||||
expect(recognized, const <String>['down', 'start']);
|
||||
});
|
||||
|
||||
testGesture('Up cancels long press', (GestureTester tester) {
|
||||
longPress.addPointer(down);
|
||||
gesture.addPointer(down);
|
||||
tester.closeArena(5);
|
||||
expect(longPressDown, isFalse);
|
||||
expect(recognized, const <String>[]);
|
||||
tester.route(down);
|
||||
expect(longPressDown, isFalse);
|
||||
expect(recognized, const <String>['down']);
|
||||
tester.async.elapse(const Duration(milliseconds: 300));
|
||||
expect(longPressDown, isFalse);
|
||||
expect(recognized, const <String>['down']);
|
||||
tester.route(up);
|
||||
expect(longPressDown, isFalse);
|
||||
expect(recognized, const <String>['down', 'cancel']);
|
||||
tester.async.elapse(const Duration(seconds: 1));
|
||||
expect(longPressDown, isFalse);
|
||||
|
||||
longPress.dispose();
|
||||
gesture.dispose();
|
||||
expect(recognized, const <String>['down', 'cancel']);
|
||||
});
|
||||
|
||||
testGesture('Moving before accept cancels', (GestureTester tester) {
|
||||
longPress.addPointer(down);
|
||||
gesture.addPointer(down);
|
||||
tester.closeArena(5);
|
||||
expect(longPressDown, isFalse);
|
||||
expect(recognized, const <String>[]);
|
||||
tester.route(down);
|
||||
expect(longPressDown, isFalse);
|
||||
expect(recognized, const <String>['down']);
|
||||
tester.async.elapse(const Duration(milliseconds: 300));
|
||||
expect(longPressDown, isFalse);
|
||||
expect(recognized, const <String>['down']);
|
||||
tester.route(move);
|
||||
expect(longPressDown, isFalse);
|
||||
expect(recognized, const <String>['down', 'cancel']);
|
||||
tester.async.elapse(const Duration(seconds: 1));
|
||||
tester.route(up);
|
||||
tester.async.elapse(const Duration(milliseconds: 300));
|
||||
expect(longPressDown, isFalse);
|
||||
expect(longPressUp, isFalse);
|
||||
|
||||
longPress.dispose();
|
||||
expect(recognized, const <String>['down', 'cancel']);
|
||||
gesture.dispose();
|
||||
expect(recognized, const <String>['down', 'cancel']);
|
||||
});
|
||||
|
||||
testGesture('Moving after accept is ok', (GestureTester tester) {
|
||||
longPress.addPointer(down);
|
||||
gesture.addPointer(down);
|
||||
tester.closeArena(5);
|
||||
expect(longPressDown, isFalse);
|
||||
expect(recognized, const <String>[]);
|
||||
tester.route(down);
|
||||
expect(longPressDown, isFalse);
|
||||
expect(recognized, const <String>['down']);
|
||||
tester.async.elapse(const Duration(seconds: 1));
|
||||
expect(longPressDown, isTrue);
|
||||
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(longPressDown, isTrue);
|
||||
expect(longPressUp, isTrue);
|
||||
|
||||
longPress.dispose();
|
||||
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();
|
||||
|
||||
bool tapDownRecognized = false;
|
||||
tap.onTapDown = (_) {
|
||||
tapDownRecognized = true;
|
||||
recognized.add('tap_down');
|
||||
};
|
||||
|
||||
tap.addPointer(down);
|
||||
longPress.addPointer(down);
|
||||
gesture.addPointer(down);
|
||||
tester.closeArena(5);
|
||||
expect(tapDownRecognized, isFalse);
|
||||
expect(longPressDown, isFalse);
|
||||
expect(recognized, const <String>[]);
|
||||
tester.route(down);
|
||||
expect(tapDownRecognized, isFalse);
|
||||
expect(longPressDown, isFalse);
|
||||
expect(recognized, const <String>['down']);
|
||||
tester.async.elapse(const Duration(milliseconds: 300));
|
||||
expect(tapDownRecognized, isTrue);
|
||||
expect(longPressDown, isFalse);
|
||||
expect(recognized, const <String>['down', 'tap_down']);
|
||||
tester.async.elapse(const Duration(milliseconds: 700));
|
||||
expect(tapDownRecognized, isTrue);
|
||||
expect(longPressDown, isTrue);
|
||||
|
||||
expect(recognized, const <String>['down', 'tap_down', 'start']);
|
||||
tap.dispose();
|
||||
longPress.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;
|
||||
|
||||
bool dragStartRecognized = false;
|
||||
drag.onStart = (DragStartDetails details) {
|
||||
expect(isDangerousStack, isFalse);
|
||||
dragStartRecognized = true;
|
||||
recognized.add('drag_start');
|
||||
};
|
||||
|
||||
drag.addPointer(down);
|
||||
longPress.addPointer(down);
|
||||
gesture.addPointer(down);
|
||||
tester.closeArena(5);
|
||||
expect(dragStartRecognized, isFalse);
|
||||
expect(longPressDown, isFalse);
|
||||
expect(recognized, const <String>[]);
|
||||
tester.route(down);
|
||||
expect(dragStartRecognized, isFalse);
|
||||
expect(longPressDown, isFalse);
|
||||
expect(recognized, const <String>['down']);
|
||||
tester.async.elapse(const Duration(milliseconds: 300));
|
||||
expect(dragStartRecognized, isFalse);
|
||||
expect(longPressDown, isFalse);
|
||||
expect(recognized, const <String>['down']);
|
||||
isDangerousStack = true;
|
||||
longPress.dispose();
|
||||
gesture.dispose();
|
||||
isDangerousStack = false;
|
||||
expect(dragStartRecognized, isFalse);
|
||||
expect(longPressDown, isFalse);
|
||||
expect(recognized, const <String>['down', 'cancel']);
|
||||
tester.async.flushMicrotasks();
|
||||
expect(dragStartRecognized, isTrue);
|
||||
expect(longPressDown, isFalse);
|
||||
expect(recognized, const <String>['down', 'cancel', 'drag_start']);
|
||||
drag.dispose();
|
||||
});
|
||||
|
||||
testGesture('Should recognize long press up', (GestureTester tester) {
|
||||
bool longPressUpRecognized = false;
|
||||
longPress.onLongPressUp = () {
|
||||
longPressUpRecognized = true;
|
||||
};
|
||||
|
||||
longPress.addPointer(down);
|
||||
gesture.addPointer(down);
|
||||
tester.closeArena(5);
|
||||
expect(longPressUpRecognized, isFalse);
|
||||
expect(recognized, const <String>[]);
|
||||
tester.route(down); // kLongPressTimeout = 500;
|
||||
expect(longPressUpRecognized, isFalse);
|
||||
expect(recognized, const <String>['down']);
|
||||
tester.async.elapse(const Duration(milliseconds: 300));
|
||||
expect(longPressUpRecognized, isFalse);
|
||||
expect(recognized, const <String>['down']);
|
||||
tester.async.elapse(const Duration(milliseconds: 700));
|
||||
expect(recognized, const <String>['down', 'start']);
|
||||
tester.route(up);
|
||||
expect(longPressUpRecognized, isTrue);
|
||||
|
||||
longPress.dispose();
|
||||
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) {
|
||||
longPress.addPointer(const PointerDownEvent(
|
||||
gesture.addPointer(const PointerDownEvent(
|
||||
pointer: 5,
|
||||
kind: PointerDeviceKind.mouse,
|
||||
buttons: kSecondaryMouseButton | kTertiaryButton,
|
||||
position: Offset(10, 10),
|
||||
));
|
||||
tester.closeArena(5);
|
||||
expect(longPressDown, isFalse);
|
||||
expect(recognized, const <String>[]);
|
||||
tester.route(down);
|
||||
expect(longPressDown, isFalse);
|
||||
expect(recognized, const <String>[]);
|
||||
tester.async.elapse(const Duration(milliseconds: 1000));
|
||||
expect(longPressDown, isFalse);
|
||||
expect(recognized, const <String>[]);
|
||||
tester.route(up);
|
||||
expect(longPressUp, isFalse);
|
||||
|
||||
longPress.dispose();
|
||||
expect(recognized, const <String>[]);
|
||||
gesture.dispose();
|
||||
expect(recognized, const <String>[]);
|
||||
});
|
||||
|
||||
testGesture('Should cancel long press when buttons change before acceptance', (GestureTester tester) {
|
||||
longPress.addPointer(down);
|
||||
gesture.addPointer(down);
|
||||
tester.closeArena(5);
|
||||
expect(longPressDown, isFalse);
|
||||
expect(recognized, const <String>[]);
|
||||
tester.route(down);
|
||||
expect(longPressDown, isFalse);
|
||||
expect(recognized, const <String>['down']);
|
||||
tester.async.elapse(const Duration(milliseconds: 300));
|
||||
expect(longPressDown, isFalse);
|
||||
expect(recognized, const <String>['down']);
|
||||
tester.route(const PointerMoveEvent(
|
||||
pointer: 5,
|
||||
kind: PointerDeviceKind.mouse,
|
||||
buttons: kTertiaryButton,
|
||||
position: Offset(10, 10),
|
||||
));
|
||||
expect(longPressDown, isFalse);
|
||||
expect(recognized, const <String>['down', 'cancel']);
|
||||
tester.async.elapse(const Duration(milliseconds: 700));
|
||||
expect(longPressDown, isFalse);
|
||||
expect(recognized, const <String>['down', 'cancel']);
|
||||
tester.route(up);
|
||||
expect(longPressUp, isFalse);
|
||||
|
||||
longPress.dispose();
|
||||
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) {
|
||||
longPress = LongPressGestureRecognizer(kind: PointerDeviceKind.touch)..onLongPress = () {};
|
||||
gesture = LongPressGestureRecognizer(kind: PointerDeviceKind.touch);
|
||||
setUpHandlers();
|
||||
|
||||
// Accept a long-press gesture
|
||||
longPress.addPointer(down);
|
||||
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)
|
||||
longPress.addPointer(const PointerDownEvent(
|
||||
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']);
|
||||
});
|
||||
});
|
||||
|
||||
group('long press drag', () {
|
||||
late LongPressGestureRecognizer longPressDrag;
|
||||
late bool longPressStart;
|
||||
late bool longPressUp;
|
||||
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(() {
|
||||
longPressDrag = LongPressGestureRecognizer();
|
||||
longPressStart = false;
|
||||
longPressDrag.onLongPressStart = (LongPressStartDetails details) {
|
||||
longPressStart = true;
|
||||
};
|
||||
longPressUp = false;
|
||||
longPressDrag.onLongPressEnd = (LongPressEndDetails details) {
|
||||
longPressUp = true;
|
||||
};
|
||||
longPressDragUpdate = null;
|
||||
longPressDrag.onLongPressMoveUpdate = (LongPressMoveUpdateDetails details) {
|
||||
longPressDragUpdate = details.globalPosition;
|
||||
};
|
||||
gesture = LongPressGestureRecognizer();
|
||||
setUpHandlers();
|
||||
recognized = <String>[];
|
||||
});
|
||||
|
||||
testGesture('Should recognize long press down', (GestureTester tester) {
|
||||
longPressDrag.addPointer(down);
|
||||
gesture.addPointer(down);
|
||||
tester.closeArena(5);
|
||||
expect(longPressStart, isFalse);
|
||||
expect(recognized, const <String>[]);
|
||||
tester.route(down);
|
||||
expect(longPressStart, isFalse);
|
||||
expect(recognized, const <String>['down']);
|
||||
tester.async.elapse(const Duration(milliseconds: 300));
|
||||
expect(longPressStart, isFalse);
|
||||
expect(recognized, const <String>['down']);
|
||||
tester.async.elapse(const Duration(milliseconds: 700));
|
||||
expect(longPressStart, isTrue);
|
||||
|
||||
longPressDrag.dispose();
|
||||
expect(recognized, const <String>['down', 'start']);
|
||||
gesture.dispose();
|
||||
expect(recognized, const <String>['down', 'start']);
|
||||
});
|
||||
|
||||
testGesture('Short up cancels long press', (GestureTester tester) {
|
||||
longPressDrag.addPointer(down);
|
||||
gesture.addPointer(down);
|
||||
tester.closeArena(5);
|
||||
expect(longPressStart, isFalse);
|
||||
expect(recognized, const <String>[]);
|
||||
tester.route(down);
|
||||
expect(longPressStart, isFalse);
|
||||
expect(recognized, const <String>['down']);
|
||||
tester.async.elapse(const Duration(milliseconds: 300));
|
||||
expect(longPressStart, isFalse);
|
||||
expect(recognized, const <String>['down']);
|
||||
tester.route(up);
|
||||
expect(longPressStart, isFalse);
|
||||
expect(recognized, const <String>['down', 'cancel']);
|
||||
tester.async.elapse(const Duration(seconds: 1));
|
||||
expect(longPressStart, isFalse);
|
||||
|
||||
longPressDrag.dispose();
|
||||
expect(recognized, const <String>['down', 'cancel']);
|
||||
gesture.dispose();
|
||||
expect(recognized, const <String>['down', 'cancel']);
|
||||
});
|
||||
|
||||
testGesture('Moving before accept cancels', (GestureTester tester) {
|
||||
longPressDrag.addPointer(down);
|
||||
gesture.addPointer(down);
|
||||
tester.closeArena(5);
|
||||
expect(longPressStart, isFalse);
|
||||
expect(recognized, const <String>[]);
|
||||
tester.route(down);
|
||||
expect(longPressStart, isFalse);
|
||||
expect(recognized, const <String>['down']);
|
||||
tester.async.elapse(const Duration(milliseconds: 300));
|
||||
expect(longPressStart, isFalse);
|
||||
expect(recognized, const <String>['down']);
|
||||
tester.route(move);
|
||||
expect(longPressStart, isFalse);
|
||||
expect(recognized, const <String>['down', 'cancel']);
|
||||
tester.async.elapse(const Duration(seconds: 1));
|
||||
tester.route(up);
|
||||
tester.async.elapse(const Duration(milliseconds: 300));
|
||||
expect(longPressStart, isFalse);
|
||||
expect(longPressUp, isFalse);
|
||||
|
||||
longPressDrag.dispose();
|
||||
expect(recognized, const <String>['down', 'cancel']);
|
||||
gesture.dispose();
|
||||
expect(recognized, const <String>['down', 'cancel']);
|
||||
});
|
||||
|
||||
testGesture('Moving after accept does not cancel', (GestureTester tester) {
|
||||
longPressDrag.addPointer(down);
|
||||
gesture.addPointer(down);
|
||||
tester.closeArena(5);
|
||||
expect(longPressStart, isFalse);
|
||||
expect(recognized, const <String>[]);
|
||||
tester.route(down);
|
||||
expect(longPressStart, isFalse);
|
||||
expect(recognized, const <String>['down']);
|
||||
tester.async.elapse(const Duration(seconds: 1));
|
||||
expect(longPressStart, isTrue);
|
||||
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(longPressStart, isTrue);
|
||||
expect(longPressUp, isTrue);
|
||||
|
||||
longPressDrag.dispose();
|
||||
expect(recognized, const <String>['down', 'start', 'move', 'end']);
|
||||
gesture.dispose();
|
||||
expect(recognized, const <String>['down', 'start', 'move', 'end']);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -411,12 +409,17 @@ void main() {
|
|||
position: Offset(10, 10),
|
||||
);
|
||||
|
||||
late LongPressGestureRecognizer gesture;
|
||||
final List<String> recognized = <String>[];
|
||||
|
||||
late LongPressGestureRecognizer longPress;
|
||||
|
||||
setUp(() {
|
||||
longPress = LongPressGestureRecognizer()
|
||||
gesture = LongPressGestureRecognizer()
|
||||
..onLongPressDown = (LongPressDownDetails details) {
|
||||
recognized.add('down');
|
||||
}
|
||||
..onLongPressCancel = () {
|
||||
recognized.add('cancel');
|
||||
}
|
||||
..onLongPressStart = (LongPressStartDetails details) {
|
||||
recognized.add('start');
|
||||
}
|
||||
|
@ -426,40 +429,43 @@ void main() {
|
|||
});
|
||||
|
||||
tearDown(() {
|
||||
longPress.dispose();
|
||||
gesture.dispose();
|
||||
recognized.clear();
|
||||
});
|
||||
|
||||
testGesture('Should cancel long press when buttons change before acceptance', (GestureTester tester) {
|
||||
// First press
|
||||
longPress.addPointer(down);
|
||||
gesture.addPointer(down);
|
||||
tester.closeArena(down.pointer);
|
||||
tester.route(down);
|
||||
tester.async.elapse(const Duration(milliseconds: 300));
|
||||
tester.route(moveR);
|
||||
expect(recognized, <String>[]);
|
||||
expect(recognized, const <String>['down', 'cancel']);
|
||||
tester.async.elapse(const Duration(milliseconds: 700));
|
||||
tester.route(up);
|
||||
expect(recognized, <String>[]);
|
||||
expect(recognized, const <String>['down', 'cancel']);
|
||||
});
|
||||
|
||||
testGesture('Buttons change before acceptance should not prevent the next long press', (GestureTester tester) {
|
||||
// First press
|
||||
longPress.addPointer(down);
|
||||
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
|
||||
longPress.addPointer(down2);
|
||||
gesture.addPointer(down2);
|
||||
tester.closeArena(down2.pointer);
|
||||
tester.route(down2);
|
||||
expect(recognized, <String>['down']);
|
||||
tester.async.elapse(const Duration(milliseconds: 1000));
|
||||
expect(recognized, <String>['start']);
|
||||
expect(recognized, <String>['down', 'start']);
|
||||
recognized.clear();
|
||||
|
||||
tester.route(up2);
|
||||
|
@ -468,11 +474,11 @@ void main() {
|
|||
|
||||
testGesture('Should cancel long press when buttons change after acceptance', (GestureTester tester) {
|
||||
// First press
|
||||
longPress.addPointer(down);
|
||||
gesture.addPointer(down);
|
||||
tester.closeArena(down.pointer);
|
||||
tester.route(down);
|
||||
tester.async.elapse(const Duration(milliseconds: 1000));
|
||||
expect(recognized, <String>['start']);
|
||||
expect(recognized, <String>['down', 'start']);
|
||||
recognized.clear();
|
||||
|
||||
tester.route(moveR);
|
||||
|
@ -483,7 +489,7 @@ void main() {
|
|||
|
||||
testGesture('Buttons change after acceptance should not prevent the next long press', (GestureTester tester) {
|
||||
// First press
|
||||
longPress.addPointer(down);
|
||||
gesture.addPointer(down);
|
||||
tester.closeArena(down.pointer);
|
||||
tester.route(down);
|
||||
tester.async.elapse(const Duration(milliseconds: 1000));
|
||||
|
@ -492,11 +498,11 @@ void main() {
|
|||
recognized.clear();
|
||||
|
||||
// Second press
|
||||
longPress.addPointer(down2);
|
||||
gesture.addPointer(down2);
|
||||
tester.closeArena(down2.pointer);
|
||||
tester.route(down2);
|
||||
tester.async.elapse(const Duration(milliseconds: 1000));
|
||||
expect(recognized, <String>['start']);
|
||||
expect(recognized, <String>['down', 'start']);
|
||||
recognized.clear();
|
||||
|
||||
tester.route(up2);
|
||||
|
|
|
@ -41,6 +41,7 @@ void main() {
|
|||
editable.attach(owner);
|
||||
// This should register pointer into GestureBinding.instance.pointerRouter.
|
||||
editable.handleEvent(const PointerDownEvent(), BoxHitTestEntry(editable, const Offset(10,10)));
|
||||
GestureBinding.instance!.pointerRouter.route(const PointerDownEvent());
|
||||
expect(spy.routeCount, greaterThan(0));
|
||||
editable.detach();
|
||||
expect(spy.routeCount, 0);
|
||||
|
|
|
@ -922,6 +922,7 @@ void main() {
|
|||
// Move back to reset.
|
||||
await dragScrollbarGesture.moveBy(const Offset(0.0, -scrollAmount));
|
||||
await tester.pumpAndSettle();
|
||||
await dragScrollbarGesture.up();
|
||||
expect(scrollController.offset, 0.0);
|
||||
expect(
|
||||
find.byType(RawScrollbar),
|
||||
|
|
Loading…
Reference in a new issue