992 lines
40 KiB
Dart
992 lines
40 KiB
Dart
import 'package:flutter/widgets.dart';
|
|
import 'package:flutter/gestures.dart';
|
|
import 'package:flutter/rendering.dart';
|
|
|
|
// as of Flutter v3.27.1, `GestureDetector` does not allow setting long press delay
|
|
// adapted from Flutter `GestureDetector` in `/widgets/gesture_detector.dart`
|
|
class AGestureDetector extends StatelessWidget {
|
|
/// Creates a widget that detects gestures.
|
|
///
|
|
/// Pan and scale callbacks cannot be used simultaneously because scale is a
|
|
/// superset of pan. Use the scale callbacks instead.
|
|
///
|
|
/// Horizontal and vertical drag callbacks cannot be used simultaneously
|
|
/// because a combination of a horizontal and vertical drag is a pan.
|
|
/// Use the pan callbacks instead.
|
|
///
|
|
/// {@youtube 560 315 https://www.youtube.com/watch?v=WhVXkCFPmK4}
|
|
///
|
|
/// By default, gesture detectors contribute semantic information to the tree
|
|
/// that is used by assistive technology.
|
|
AGestureDetector({
|
|
super.key,
|
|
this.child,
|
|
this.onTapDown,
|
|
this.onTapUp,
|
|
this.onTap,
|
|
this.onTapCancel,
|
|
this.onSecondaryTap,
|
|
this.onSecondaryTapDown,
|
|
this.onSecondaryTapUp,
|
|
this.onSecondaryTapCancel,
|
|
this.onTertiaryTapDown,
|
|
this.onTertiaryTapUp,
|
|
this.onTertiaryTapCancel,
|
|
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,
|
|
this.onVerticalDragEnd,
|
|
this.onVerticalDragCancel,
|
|
this.onHorizontalDragDown,
|
|
this.onHorizontalDragStart,
|
|
this.onHorizontalDragUpdate,
|
|
this.onHorizontalDragEnd,
|
|
this.onHorizontalDragCancel,
|
|
this.onForcePressStart,
|
|
this.onForcePressPeak,
|
|
this.onForcePressUpdate,
|
|
this.onForcePressEnd,
|
|
this.onPanDown,
|
|
this.onPanStart,
|
|
this.onPanUpdate,
|
|
this.onPanEnd,
|
|
this.onPanCancel,
|
|
this.onScaleStart,
|
|
this.onScaleUpdate,
|
|
this.onScaleEnd,
|
|
this.behavior,
|
|
this.excludeFromSemantics = false,
|
|
this.dragStartBehavior = DragStartBehavior.start,
|
|
this.trackpadScrollCausesScale = false,
|
|
this.trackpadScrollToScaleFactor = kDefaultTrackpadScrollToScaleFactor,
|
|
this.supportedDevices,
|
|
this.longPressTimeout = kLongPressTimeout,
|
|
}) : assert(() {
|
|
final bool haveVerticalDrag = onVerticalDragStart != null || onVerticalDragUpdate != null || onVerticalDragEnd != null;
|
|
final bool haveHorizontalDrag = onHorizontalDragStart != null || onHorizontalDragUpdate != null || onHorizontalDragEnd != null;
|
|
final bool havePan = onPanStart != null || onPanUpdate != null || onPanEnd != null;
|
|
final bool haveScale = onScaleStart != null || onScaleUpdate != null || onScaleEnd != null;
|
|
if (havePan || haveScale) {
|
|
if (havePan && haveScale) {
|
|
throw FlutterError.fromParts(<DiagnosticsNode>[
|
|
ErrorSummary('Incorrect GestureDetector arguments.'),
|
|
ErrorDescription(
|
|
'Having both a pan gesture recognizer and a scale gesture recognizer is redundant; scale is a superset of pan.',
|
|
),
|
|
ErrorHint('Just use the scale gesture recognizer.'),
|
|
]);
|
|
}
|
|
final String recognizer = havePan ? 'pan' : 'scale';
|
|
if (haveVerticalDrag && haveHorizontalDrag) {
|
|
throw FlutterError(
|
|
'Incorrect GestureDetector arguments.\n'
|
|
'Simultaneously having a vertical drag gesture recognizer, a horizontal drag gesture recognizer, and a $recognizer gesture recognizer '
|
|
'will result in the $recognizer gesture recognizer being ignored, since the other two will catch all drags.',
|
|
);
|
|
}
|
|
}
|
|
return true;
|
|
}());
|
|
|
|
/// The widget below this widget in the tree.
|
|
///
|
|
/// {@macro flutter.widgets.ProxyWidget.child}
|
|
final Widget? child;
|
|
|
|
/// A pointer that might cause a tap with a primary button has contacted the
|
|
/// screen at a particular location.
|
|
///
|
|
/// This is called after a short timeout, even if the winning gesture has not
|
|
/// yet been selected. If the tap gesture wins, [onTapUp] will be called,
|
|
/// otherwise [onTapCancel] will be called.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [kPrimaryButton], the button this callback responds to.
|
|
final GestureTapDownCallback? onTapDown;
|
|
|
|
/// A pointer that will trigger a tap with a primary button has stopped
|
|
/// contacting the screen at a particular location.
|
|
///
|
|
/// This triggers immediately before [onTap] in the case of the tap gesture
|
|
/// winning. If the tap gesture did not win, [onTapCancel] is called instead.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [kPrimaryButton], the button this callback responds to.
|
|
final GestureTapUpCallback? onTapUp;
|
|
|
|
/// A tap with a primary button has occurred.
|
|
///
|
|
/// This triggers when the tap gesture wins. If the tap gesture did not win,
|
|
/// [onTapCancel] is called instead.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [kPrimaryButton], the button this callback responds to.
|
|
/// * [onTapUp], which is called at the same time but includes details
|
|
/// regarding the pointer position.
|
|
final GestureTapCallback? onTap;
|
|
|
|
/// The pointer that previously triggered [onTapDown] will not end up causing
|
|
/// a tap.
|
|
///
|
|
/// This is called after [onTapDown], and instead of [onTapUp] and [onTap], if
|
|
/// the tap gesture did not win.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [kPrimaryButton], the button this callback responds to.
|
|
final GestureTapCancelCallback? onTapCancel;
|
|
|
|
/// A tap with a secondary button has occurred.
|
|
///
|
|
/// This triggers when the tap gesture wins. If the tap gesture did not win,
|
|
/// [onSecondaryTapCancel] is called instead.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [kSecondaryButton], the button this callback responds to.
|
|
/// * [onSecondaryTapUp], which is called at the same time but includes details
|
|
/// regarding the pointer position.
|
|
final GestureTapCallback? onSecondaryTap;
|
|
|
|
/// A pointer that might cause a tap with a secondary button has contacted the
|
|
/// screen at a particular location.
|
|
///
|
|
/// This is called after a short timeout, even if the winning gesture has not
|
|
/// yet been selected. If the tap gesture wins, [onSecondaryTapUp] will be
|
|
/// called, otherwise [onSecondaryTapCancel] will be called.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [kSecondaryButton], the button this callback responds to.
|
|
final GestureTapDownCallback? onSecondaryTapDown;
|
|
|
|
/// A pointer that will trigger a tap with a secondary button has stopped
|
|
/// contacting the screen at a particular location.
|
|
///
|
|
/// This triggers in the case of the tap gesture winning. If the tap gesture
|
|
/// did not win, [onSecondaryTapCancel] is called instead.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [onSecondaryTap], a handler triggered right after this one that doesn't
|
|
/// pass any details about the tap.
|
|
/// * [kSecondaryButton], the button this callback responds to.
|
|
final GestureTapUpCallback? onSecondaryTapUp;
|
|
|
|
/// The pointer that previously triggered [onSecondaryTapDown] will not end up
|
|
/// causing a tap.
|
|
///
|
|
/// This is called after [onSecondaryTapDown], and instead of
|
|
/// [onSecondaryTapUp], if the tap gesture did not win.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [kSecondaryButton], the button this callback responds to.
|
|
final GestureTapCancelCallback? onSecondaryTapCancel;
|
|
|
|
/// A pointer that might cause a tap with a tertiary button has contacted the
|
|
/// screen at a particular location.
|
|
///
|
|
/// This is called after a short timeout, even if the winning gesture has not
|
|
/// yet been selected. If the tap gesture wins, [onTertiaryTapUp] will be
|
|
/// called, otherwise [onTertiaryTapCancel] will be called.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [kTertiaryButton], the button this callback responds to.
|
|
final GestureTapDownCallback? onTertiaryTapDown;
|
|
|
|
/// A pointer that will trigger a tap with a tertiary button has stopped
|
|
/// contacting the screen at a particular location.
|
|
///
|
|
/// This triggers in the case of the tap gesture winning. If the tap gesture
|
|
/// did not win, [onTertiaryTapCancel] is called instead.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [kTertiaryButton], the button this callback responds to.
|
|
final GestureTapUpCallback? onTertiaryTapUp;
|
|
|
|
/// The pointer that previously triggered [onTertiaryTapDown] will not end up
|
|
/// causing a tap.
|
|
///
|
|
/// This is called after [onTertiaryTapDown], and instead of
|
|
/// [onTertiaryTapUp], if the tap gesture did not win.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [kTertiaryButton], the button this callback responds to.
|
|
final GestureTapCancelCallback? onTertiaryTapCancel;
|
|
|
|
/// A pointer that might cause a double tap has contacted the screen at a
|
|
/// particular location.
|
|
///
|
|
/// Triggered immediately after the down event of the second tap.
|
|
///
|
|
/// If the user completes the double tap and the gesture wins, [onDoubleTap]
|
|
/// will be called after this callback. Otherwise, [onDoubleTapCancel] will
|
|
/// be called after this callback.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [kPrimaryButton], the button this callback responds to.
|
|
final GestureTapDownCallback? onDoubleTapDown;
|
|
|
|
/// The user has tapped the screen with a primary button at the same location
|
|
/// twice in quick succession.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [kPrimaryButton], the button this callback responds to.
|
|
final GestureTapCallback? onDoubleTap;
|
|
|
|
/// The pointer that previously triggered [onDoubleTapDown] will not end up
|
|
/// causing a double tap.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [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.
|
|
/// * [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.
|
|
///
|
|
/// 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.
|
|
/// * [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.
|
|
///
|
|
/// 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.
|
|
/// * [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.
|
|
/// * [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.
|
|
/// * [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
|
|
/// 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)
|
|
/// [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.
|
|
/// * [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.
|
|
///
|
|
/// 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.
|
|
/// * [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.
|
|
/// * [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.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [kPrimaryButton], the button this callback responds to.
|
|
final GestureDragDownCallback? onVerticalDragDown;
|
|
|
|
/// A pointer has contacted the screen with a primary button and has begun to
|
|
/// move vertically.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [kPrimaryButton], the button this callback responds to.
|
|
final GestureDragStartCallback? onVerticalDragStart;
|
|
|
|
/// A pointer that is in contact with the screen with a primary button and
|
|
/// moving vertically has moved in the vertical direction.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [kPrimaryButton], the button this callback responds to.
|
|
final GestureDragUpdateCallback? onVerticalDragUpdate;
|
|
|
|
/// A pointer that was previously in contact with the screen with a primary
|
|
/// button and moving vertically is no longer in contact with the screen and
|
|
/// was moving at a specific velocity when it stopped contacting the screen.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [kPrimaryButton], the button this callback responds to.
|
|
final GestureDragEndCallback? onVerticalDragEnd;
|
|
|
|
/// The pointer that previously triggered [onVerticalDragDown] did not
|
|
/// complete.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [kPrimaryButton], the button this callback responds to.
|
|
final GestureDragCancelCallback? onVerticalDragCancel;
|
|
|
|
/// A pointer has contacted the screen with a primary button and might begin
|
|
/// to move horizontally.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [kPrimaryButton], the button this callback responds to.
|
|
final GestureDragDownCallback? onHorizontalDragDown;
|
|
|
|
/// A pointer has contacted the screen with a primary button and has begun to
|
|
/// move horizontally.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [kPrimaryButton], the button this callback responds to.
|
|
final GestureDragStartCallback? onHorizontalDragStart;
|
|
|
|
/// A pointer that is in contact with the screen with a primary button and
|
|
/// moving horizontally has moved in the horizontal direction.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [kPrimaryButton], the button this callback responds to.
|
|
final GestureDragUpdateCallback? onHorizontalDragUpdate;
|
|
|
|
/// A pointer that was previously in contact with the screen with a primary
|
|
/// button and moving horizontally is no longer in contact with the screen and
|
|
/// was moving at a specific velocity when it stopped contacting the screen.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [kPrimaryButton], the button this callback responds to.
|
|
final GestureDragEndCallback? onHorizontalDragEnd;
|
|
|
|
/// The pointer that previously triggered [onHorizontalDragDown] did not
|
|
/// complete.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [kPrimaryButton], the button this callback responds to.
|
|
final GestureDragCancelCallback? onHorizontalDragCancel;
|
|
|
|
/// A pointer has contacted the screen with a primary button and might begin
|
|
/// to move.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [kPrimaryButton], the button this callback responds to.
|
|
final GestureDragDownCallback? onPanDown;
|
|
|
|
/// A pointer has contacted the screen with a primary button and has begun to
|
|
/// move.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [kPrimaryButton], the button this callback responds to.
|
|
final GestureDragStartCallback? onPanStart;
|
|
|
|
/// A pointer that is in contact with the screen with a primary button and
|
|
/// moving has moved again.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [kPrimaryButton], the button this callback responds to.
|
|
final GestureDragUpdateCallback? onPanUpdate;
|
|
|
|
/// A pointer that was previously in contact with the screen with a primary
|
|
/// button and moving is no longer in contact with the screen and was moving
|
|
/// at a specific velocity when it stopped contacting the screen.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [kPrimaryButton], the button this callback responds to.
|
|
final GestureDragEndCallback? onPanEnd;
|
|
|
|
/// The pointer that previously triggered [onPanDown] did not complete.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [kPrimaryButton], the button this callback responds to.
|
|
final GestureDragCancelCallback? onPanCancel;
|
|
|
|
/// The pointers in contact with the screen have established a focal point and
|
|
/// initial scale of 1.0.
|
|
final GestureScaleStartCallback? onScaleStart;
|
|
|
|
/// The pointers in contact with the screen have indicated a new focal point
|
|
/// and/or scale.
|
|
final GestureScaleUpdateCallback? onScaleUpdate;
|
|
|
|
/// The pointers are no longer in contact with the screen.
|
|
final GestureScaleEndCallback? onScaleEnd;
|
|
|
|
/// The pointer is in contact with the screen and has pressed with sufficient
|
|
/// force to initiate a force press. The amount of force is at least
|
|
/// [ForcePressGestureRecognizer.startPressure].
|
|
///
|
|
/// This callback will only be fired on devices with pressure
|
|
/// detecting screens.
|
|
final GestureForcePressStartCallback? onForcePressStart;
|
|
|
|
/// The pointer is in contact with the screen and has pressed with the maximum
|
|
/// force. The amount of force is at least
|
|
/// [ForcePressGestureRecognizer.peakPressure].
|
|
///
|
|
/// This callback will only be fired on devices with pressure
|
|
/// detecting screens.
|
|
final GestureForcePressPeakCallback? onForcePressPeak;
|
|
|
|
/// A pointer is in contact with the screen, has previously passed the
|
|
/// [ForcePressGestureRecognizer.startPressure] and is either moving on the
|
|
/// plane of the screen, pressing the screen with varying forces or both
|
|
/// simultaneously.
|
|
///
|
|
/// This callback will only be fired on devices with pressure
|
|
/// detecting screens.
|
|
final GestureForcePressUpdateCallback? onForcePressUpdate;
|
|
|
|
/// The pointer tracked by [onForcePressStart] is no longer in contact with the screen.
|
|
///
|
|
/// This callback will only be fired on devices with pressure
|
|
/// detecting screens.
|
|
final GestureForcePressEndCallback? onForcePressEnd;
|
|
|
|
/// How this gesture detector should behave during hit testing when deciding
|
|
/// how the hit test propagates to children and whether to consider targets
|
|
/// behind this one.
|
|
///
|
|
/// This defaults to [HitTestBehavior.deferToChild] if [child] is not null and
|
|
/// [HitTestBehavior.translucent] if child is null.
|
|
///
|
|
/// See [HitTestBehavior] for the allowed values and their meanings.
|
|
final HitTestBehavior? behavior;
|
|
|
|
/// Whether to exclude these gestures from the semantics tree. For
|
|
/// example, the long-press gesture for showing a tooltip is
|
|
/// excluded because the tooltip itself is included in the semantics
|
|
/// tree directly and so having a gesture to show it would result in
|
|
/// duplication of information.
|
|
final bool excludeFromSemantics;
|
|
|
|
/// Determines the way that drag start behavior is handled.
|
|
///
|
|
/// If set to [DragStartBehavior.start], gesture drag behavior will
|
|
/// begin at the position where the drag gesture won the arena. If set to
|
|
/// [DragStartBehavior.down] it will begin at the position where a down event
|
|
/// is first detected.
|
|
///
|
|
/// In general, setting this to [DragStartBehavior.start] will make drag
|
|
/// animation smoother and setting it to [DragStartBehavior.down] will make
|
|
/// drag behavior feel slightly more reactive.
|
|
///
|
|
/// By default, the drag start behavior is [DragStartBehavior.start].
|
|
///
|
|
/// Only the [DragGestureRecognizer.onStart] callbacks for the
|
|
/// [VerticalDragGestureRecognizer], [HorizontalDragGestureRecognizer] and
|
|
/// [PanGestureRecognizer] are affected by this setting.
|
|
///
|
|
/// See also:
|
|
///
|
|
/// * [DragGestureRecognizer.dragStartBehavior], which gives an example for the different behaviors.
|
|
final DragStartBehavior dragStartBehavior;
|
|
|
|
/// The kind of devices that are allowed to be recognized.
|
|
///
|
|
/// If set to null, events from all device types will be recognized. Defaults to null.
|
|
final Set<PointerDeviceKind>? supportedDevices;
|
|
|
|
/// {@macro flutter.gestures.scale.trackpadScrollCausesScale}
|
|
final bool trackpadScrollCausesScale;
|
|
|
|
/// {@macro flutter.gestures.scale.trackpadScrollToScaleFactor}
|
|
final Offset trackpadScrollToScaleFactor;
|
|
|
|
final Duration longPressTimeout;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final Map<Type, GestureRecognizerFactory> gestures = <Type, GestureRecognizerFactory>{};
|
|
final DeviceGestureSettings? gestureSettings = MediaQuery.maybeGestureSettingsOf(context);
|
|
final ScrollBehavior configuration = ScrollConfiguration.of(context);
|
|
|
|
if (onTapDown != null || onTapUp != null || onTap != null || onTapCancel != null || onSecondaryTap != null || onSecondaryTapDown != null || onSecondaryTapUp != null || onSecondaryTapCancel != null || onTertiaryTapDown != null || onTertiaryTapUp != null || onTertiaryTapCancel != null) {
|
|
gestures[TapGestureRecognizer] = GestureRecognizerFactoryWithHandlers<TapGestureRecognizer>(
|
|
() => TapGestureRecognizer(debugOwner: this, supportedDevices: supportedDevices),
|
|
(instance) {
|
|
instance
|
|
..onTapDown = onTapDown
|
|
..onTapUp = onTapUp
|
|
..onTap = onTap
|
|
..onTapCancel = onTapCancel
|
|
..onSecondaryTap = onSecondaryTap
|
|
..onSecondaryTapDown = onSecondaryTapDown
|
|
..onSecondaryTapUp = onSecondaryTapUp
|
|
..onSecondaryTapCancel = onSecondaryTapCancel
|
|
..onTertiaryTapDown = onTertiaryTapDown
|
|
..onTertiaryTapUp = onTertiaryTapUp
|
|
..onTertiaryTapCancel = onTertiaryTapCancel
|
|
..gestureSettings = gestureSettings
|
|
..supportedDevices = supportedDevices;
|
|
},
|
|
);
|
|
}
|
|
|
|
if (onDoubleTap != null || onDoubleTapDown != null || onDoubleTapCancel != null) {
|
|
gestures[DoubleTapGestureRecognizer] = GestureRecognizerFactoryWithHandlers<DoubleTapGestureRecognizer>(
|
|
() => DoubleTapGestureRecognizer(debugOwner: this, supportedDevices: supportedDevices),
|
|
(instance) {
|
|
instance
|
|
..onDoubleTapDown = onDoubleTapDown
|
|
..onDoubleTap = onDoubleTap
|
|
..onDoubleTapCancel = onDoubleTapCancel
|
|
..gestureSettings = gestureSettings
|
|
..supportedDevices = supportedDevices;
|
|
},
|
|
);
|
|
}
|
|
|
|
if (onLongPressDown != null || onLongPressCancel != null || onLongPress != null || onLongPressStart != null || onLongPressMoveUpdate != null || onLongPressUp != null || onLongPressEnd != null || onSecondaryLongPressDown != null || onSecondaryLongPressCancel != null || onSecondaryLongPress != null || onSecondaryLongPressStart != null || onSecondaryLongPressMoveUpdate != null || onSecondaryLongPressUp != null || onSecondaryLongPressEnd != null || onTertiaryLongPressDown != null || onTertiaryLongPressCancel != null || onTertiaryLongPress != null || onTertiaryLongPressStart != null || onTertiaryLongPressMoveUpdate != null || onTertiaryLongPressUp != null || onTertiaryLongPressEnd != null) {
|
|
gestures[LongPressGestureRecognizer] = GestureRecognizerFactoryWithHandlers<LongPressGestureRecognizer>(
|
|
() => LongPressGestureRecognizer(duration: longPressTimeout, debugOwner: this, supportedDevices: supportedDevices),
|
|
(instance) {
|
|
instance
|
|
..onLongPressDown = onLongPressDown
|
|
..onLongPressCancel = onLongPressCancel
|
|
..onLongPress = onLongPress
|
|
..onLongPressStart = onLongPressStart
|
|
..onLongPressMoveUpdate = onLongPressMoveUpdate
|
|
..onLongPressUp = onLongPressUp
|
|
..onLongPressEnd = onLongPressEnd
|
|
..onSecondaryLongPressDown = onSecondaryLongPressDown
|
|
..onSecondaryLongPressCancel = onSecondaryLongPressCancel
|
|
..onSecondaryLongPress = onSecondaryLongPress
|
|
..onSecondaryLongPressStart = onSecondaryLongPressStart
|
|
..onSecondaryLongPressMoveUpdate = onSecondaryLongPressMoveUpdate
|
|
..onSecondaryLongPressUp = onSecondaryLongPressUp
|
|
..onSecondaryLongPressEnd = onSecondaryLongPressEnd
|
|
..onTertiaryLongPressDown = onTertiaryLongPressDown
|
|
..onTertiaryLongPressCancel = onTertiaryLongPressCancel
|
|
..onTertiaryLongPress = onTertiaryLongPress
|
|
..onTertiaryLongPressStart = onTertiaryLongPressStart
|
|
..onTertiaryLongPressMoveUpdate = onTertiaryLongPressMoveUpdate
|
|
..onTertiaryLongPressUp = onTertiaryLongPressUp
|
|
..onTertiaryLongPressEnd = onTertiaryLongPressEnd
|
|
..gestureSettings = gestureSettings
|
|
..supportedDevices = supportedDevices;
|
|
},
|
|
);
|
|
}
|
|
|
|
if (onVerticalDragDown != null || onVerticalDragStart != null || onVerticalDragUpdate != null || onVerticalDragEnd != null || onVerticalDragCancel != null) {
|
|
gestures[VerticalDragGestureRecognizer] = GestureRecognizerFactoryWithHandlers<VerticalDragGestureRecognizer>(
|
|
() => VerticalDragGestureRecognizer(debugOwner: this, supportedDevices: supportedDevices),
|
|
(instance) {
|
|
instance
|
|
..onDown = onVerticalDragDown
|
|
..onStart = onVerticalDragStart
|
|
..onUpdate = onVerticalDragUpdate
|
|
..onEnd = onVerticalDragEnd
|
|
..onCancel = onVerticalDragCancel
|
|
..dragStartBehavior = dragStartBehavior
|
|
..multitouchDragStrategy = configuration.getMultitouchDragStrategy(context)
|
|
..gestureSettings = gestureSettings
|
|
..supportedDevices = supportedDevices;
|
|
},
|
|
);
|
|
}
|
|
|
|
if (onHorizontalDragDown != null || onHorizontalDragStart != null || onHorizontalDragUpdate != null || onHorizontalDragEnd != null || onHorizontalDragCancel != null) {
|
|
gestures[HorizontalDragGestureRecognizer] = GestureRecognizerFactoryWithHandlers<HorizontalDragGestureRecognizer>(
|
|
() => HorizontalDragGestureRecognizer(debugOwner: this, supportedDevices: supportedDevices),
|
|
(instance) {
|
|
instance
|
|
..onDown = onHorizontalDragDown
|
|
..onStart = onHorizontalDragStart
|
|
..onUpdate = onHorizontalDragUpdate
|
|
..onEnd = onHorizontalDragEnd
|
|
..onCancel = onHorizontalDragCancel
|
|
..dragStartBehavior = dragStartBehavior
|
|
..multitouchDragStrategy = configuration.getMultitouchDragStrategy(context)
|
|
..gestureSettings = gestureSettings
|
|
..supportedDevices = supportedDevices;
|
|
},
|
|
);
|
|
}
|
|
|
|
if (onPanDown != null || onPanStart != null || onPanUpdate != null || onPanEnd != null || onPanCancel != null) {
|
|
gestures[PanGestureRecognizer] = GestureRecognizerFactoryWithHandlers<PanGestureRecognizer>(
|
|
() => PanGestureRecognizer(debugOwner: this, supportedDevices: supportedDevices),
|
|
(instance) {
|
|
instance
|
|
..onDown = onPanDown
|
|
..onStart = onPanStart
|
|
..onUpdate = onPanUpdate
|
|
..onEnd = onPanEnd
|
|
..onCancel = onPanCancel
|
|
..dragStartBehavior = dragStartBehavior
|
|
..multitouchDragStrategy = configuration.getMultitouchDragStrategy(context)
|
|
..gestureSettings = gestureSettings
|
|
..supportedDevices = supportedDevices;
|
|
},
|
|
);
|
|
}
|
|
|
|
if (onScaleStart != null || onScaleUpdate != null || onScaleEnd != null) {
|
|
gestures[ScaleGestureRecognizer] = GestureRecognizerFactoryWithHandlers<ScaleGestureRecognizer>(
|
|
() => ScaleGestureRecognizer(debugOwner: this, supportedDevices: supportedDevices),
|
|
(instance) {
|
|
instance
|
|
..onStart = onScaleStart
|
|
..onUpdate = onScaleUpdate
|
|
..onEnd = onScaleEnd
|
|
..dragStartBehavior = dragStartBehavior
|
|
..gestureSettings = gestureSettings
|
|
..trackpadScrollCausesScale = trackpadScrollCausesScale
|
|
..trackpadScrollToScaleFactor = trackpadScrollToScaleFactor
|
|
..supportedDevices = supportedDevices;
|
|
},
|
|
);
|
|
}
|
|
|
|
if (onForcePressStart != null || onForcePressPeak != null || onForcePressUpdate != null || onForcePressEnd != null) {
|
|
gestures[ForcePressGestureRecognizer] = GestureRecognizerFactoryWithHandlers<ForcePressGestureRecognizer>(
|
|
() => ForcePressGestureRecognizer(debugOwner: this, supportedDevices: supportedDevices),
|
|
(instance) {
|
|
instance
|
|
..onStart = onForcePressStart
|
|
..onPeak = onForcePressPeak
|
|
..onUpdate = onForcePressUpdate
|
|
..onEnd = onForcePressEnd
|
|
..gestureSettings = gestureSettings
|
|
..supportedDevices = supportedDevices;
|
|
},
|
|
);
|
|
}
|
|
|
|
return RawGestureDetector(
|
|
gestures: gestures,
|
|
behavior: behavior,
|
|
excludeFromSemantics: excludeFromSemantics,
|
|
child: child,
|
|
);
|
|
}
|
|
|
|
@override
|
|
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
|
super.debugFillProperties(properties);
|
|
properties.add(EnumProperty<DragStartBehavior>('startBehavior', dragStartBehavior));
|
|
}
|
|
}
|