Files
flame/lib/game/gesture_detector.dart
Erick Zanardo aa1ffced6f Linting
2020-05-13 12:03:06 -03:00

642 lines
23 KiB
Dart

import 'package:flutter/widgets.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/foundation.dart';
// From https://github.com/flutter/flutter/blob/e6b34c2b5c/packages/flutter/lib/src/widgets/gesture_detector.dart
class GestureDetector extends StatelessWidget {
/// Creates a widget that detects gestures.
///
/// Pan and scale callbacks cannot be used simultaneously because scale is a
/// superset of pan. Simply 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. Simply
/// use the pan callbacks instead.
///
/// By default, gesture detectors contribute semantic information to the tree
/// that is used by assistive technology.
GestureDetector({
Key key,
this.child,
this.onTapDown,
this.onTapUp,
this.onTap,
this.onTapCancel,
this.onSecondaryTapDown,
this.onSecondaryTapUp,
this.onSecondaryTapCancel,
this.onDoubleTap,
this.onLongPress,
this.onLongPressStart,
this.onLongPressMoveUpdate,
this.onLongPressUp,
this.onLongPressEnd,
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.onMultiTap,
this.onMultiTapUp,
this.onMultiTapDown,
this.onMultiTapCancel,
this.behavior,
this.excludeFromSemantics = false,
this.dragStartBehavior = DragStartBehavior.start,
}) : assert(excludeFromSemantics != null),
assert(dragStartBehavior != null),
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;
final bool haveTap = onTap != null ||
onTapCancel != null ||
onTapUp != null ||
onTapDown != null;
final bool haveMultiTap = onMultiTap != null ||
onMultiTapCancel != null ||
onMultiTapUp != null ||
onMultiTapDown != 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.');
}
}
if (haveTap || haveMultiTap) {
if (haveTap && haveMultiTap) {
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('Incorrect GestureDetector arguments.'),
ErrorDescription(
'Having both a tap gesture recognizer and a multi tap gesture recognizer is redundant.'),
ErrorHint('Just use one of the recognizers.')
]);
}
}
return true;
}()),
super(key: key);
/// The widget below this widget in the tree.
///
/// {@macro flutter.widgets.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 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:
///
/// * [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;
/// 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;
/// 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.
///
/// See also:
///
/// * [kPrimaryButton], the button this callback responds to.
/// * [onLongPressStart], which has the same timing but has gesture details.
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.
///
/// See also:
///
/// * [kPrimaryButton], the button this callback responds to.
/// * [onLongPress], which has the same timing but without the gesture details.
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.
final GestureLongPressMoveUpdateCallback onLongPressMoveUpdate;
/// A pointer that has triggered a long-press with a primary button has
/// stopped contacting the screen.
///
/// See also:
///
/// * [kPrimaryButton], the button this callback responds to.
/// * [onLongPressEnd], which has the same timing but has gesture details.
final GestureLongPressUpCallback onLongPressUp;
/// A pointer that has triggered a long-press with a primary button has
/// stopped contacting the screen.
///
/// See also:
///
/// * [kPrimaryButton], the button this callback responds to.
/// * [onLongPressUp], which has the same timing but without the gesture
/// details.
final GestureLongPressEndCallback onLongPressEnd;
/// 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].
///
/// Note that 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].
///
/// Note that 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.
///
/// Note that this callback will only be fired on devices with pressure
/// detecting screens.
final GestureForcePressUpdateCallback onForcePressUpdate;
/// The pointer is no longer in contact with the screen.
///
/// Note that this callback will only be fired on devices with pressure
/// detecting screens.
final GestureForcePressEndCallback onForcePressEnd;
final GestureMultiTapCallback onMultiTap;
final GestureMultiTapCancelCallback onMultiTapCancel;
final GestureMultiTapDownCallback onMultiTapDown;
final GestureMultiTapUpCallback onMultiTapUp;
/// How this gesture detector should behave during hit testing.
///
/// This defaults to [HitTestBehavior.deferToChild] if [child] is not null and
/// [HitTestBehavior.translucent] if child is null.
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 upon the detection of a drag gesture. If set to
/// [DragStartBehavior.down] it will begin when 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 [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;
@override
Widget build(BuildContext context) {
final Map<Type, GestureRecognizerFactory> gestures =
<Type, GestureRecognizerFactory>{};
if (onTapDown != null ||
onTapUp != null ||
onTap != null ||
onTapCancel != null ||
onSecondaryTapDown != null ||
onSecondaryTapUp != null ||
onSecondaryTapCancel != null) {
gestures[TapGestureRecognizer] =
GestureRecognizerFactoryWithHandlers<TapGestureRecognizer>(
() => TapGestureRecognizer(debugOwner: this),
(TapGestureRecognizer instance) {
instance
..onTapDown = onTapDown
..onTapUp = onTapUp
..onTap = onTap
..onTapCancel = onTapCancel
..onSecondaryTapDown = onSecondaryTapDown
..onSecondaryTapUp = onSecondaryTapUp
..onSecondaryTapCancel = onSecondaryTapCancel;
},
);
}
if (onDoubleTap != null) {
gestures[DoubleTapGestureRecognizer] =
GestureRecognizerFactoryWithHandlers<DoubleTapGestureRecognizer>(
() => DoubleTapGestureRecognizer(debugOwner: this),
(DoubleTapGestureRecognizer instance) {
instance.onDoubleTap = onDoubleTap;
},
);
}
if (onLongPress != null ||
onLongPressUp != null ||
onLongPressStart != null ||
onLongPressMoveUpdate != null ||
onLongPressEnd != null) {
gestures[LongPressGestureRecognizer] =
GestureRecognizerFactoryWithHandlers<LongPressGestureRecognizer>(
() => LongPressGestureRecognizer(debugOwner: this),
(LongPressGestureRecognizer instance) {
instance
..onLongPress = onLongPress
..onLongPressStart = onLongPressStart
..onLongPressMoveUpdate = onLongPressMoveUpdate
..onLongPressEnd = onLongPressEnd
..onLongPressUp = onLongPressUp;
},
);
}
if (onVerticalDragDown != null ||
onVerticalDragStart != null ||
onVerticalDragUpdate != null ||
onVerticalDragEnd != null ||
onVerticalDragCancel != null) {
gestures[VerticalDragGestureRecognizer] =
GestureRecognizerFactoryWithHandlers<VerticalDragGestureRecognizer>(
() => VerticalDragGestureRecognizer(debugOwner: this),
(VerticalDragGestureRecognizer instance) {
instance
..onDown = onVerticalDragDown
..onStart = onVerticalDragStart
..onUpdate = onVerticalDragUpdate
..onEnd = onVerticalDragEnd
..onCancel = onVerticalDragCancel
..dragStartBehavior = dragStartBehavior;
},
);
}
if (onHorizontalDragDown != null ||
onHorizontalDragStart != null ||
onHorizontalDragUpdate != null ||
onHorizontalDragEnd != null ||
onHorizontalDragCancel != null) {
gestures[HorizontalDragGestureRecognizer] =
GestureRecognizerFactoryWithHandlers<HorizontalDragGestureRecognizer>(
() => HorizontalDragGestureRecognizer(debugOwner: this),
(HorizontalDragGestureRecognizer instance) {
instance
..onDown = onHorizontalDragDown
..onStart = onHorizontalDragStart
..onUpdate = onHorizontalDragUpdate
..onEnd = onHorizontalDragEnd
..onCancel = onHorizontalDragCancel
..dragStartBehavior = dragStartBehavior;
},
);
}
if (onPanDown != null ||
onPanStart != null ||
onPanUpdate != null ||
onPanEnd != null ||
onPanCancel != null) {
gestures[PanGestureRecognizer] =
GestureRecognizerFactoryWithHandlers<PanGestureRecognizer>(
() => PanGestureRecognizer(debugOwner: this),
(PanGestureRecognizer instance) {
instance
..onDown = onPanDown
..onStart = onPanStart
..onUpdate = onPanUpdate
..onEnd = onPanEnd
..onCancel = onPanCancel
..dragStartBehavior = dragStartBehavior;
},
);
}
if (onScaleStart != null || onScaleUpdate != null || onScaleEnd != null) {
gestures[ScaleGestureRecognizer] =
GestureRecognizerFactoryWithHandlers<ScaleGestureRecognizer>(
() => ScaleGestureRecognizer(debugOwner: this),
(ScaleGestureRecognizer instance) {
instance
..onStart = onScaleStart
..onUpdate = onScaleUpdate
..onEnd = onScaleEnd;
},
);
}
if (onForcePressStart != null ||
onForcePressPeak != null ||
onForcePressUpdate != null ||
onForcePressEnd != null) {
gestures[ForcePressGestureRecognizer] =
GestureRecognizerFactoryWithHandlers<ForcePressGestureRecognizer>(
() => ForcePressGestureRecognizer(debugOwner: this),
(ForcePressGestureRecognizer instance) {
instance
..onStart = onForcePressStart
..onPeak = onForcePressPeak
..onUpdate = onForcePressUpdate
..onEnd = onForcePressEnd;
},
);
}
if (onMultiTapDown != null ||
onMultiTapUp != null ||
onMultiTap != null ||
onMultiTapCancel != null) {
gestures[MultiTapGestureRecognizer] =
GestureRecognizerFactoryWithHandlers<MultiTapGestureRecognizer>(
() => MultiTapGestureRecognizer(debugOwner: this),
(MultiTapGestureRecognizer instance) {
instance
..onTapDown = onMultiTapDown
..onTapUp = onMultiTapUp
..onTap = onMultiTap
..onTapCancel = onMultiTapCancel;
},
);
}
return RawGestureDetector(
gestures: gestures,
behavior: behavior,
excludeFromSemantics: excludeFromSemantics,
child: child,
);
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(
EnumProperty<DragStartBehavior>('startBehavior', dragStartBehavior));
}
}