gesture_detector.dart 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. import 'dart:async';
  2. import 'package:flutter/gestures.dart';
  3. import 'package:flutter/widgets.dart';
  4. class TerminalGestureDetector extends StatefulWidget {
  5. const TerminalGestureDetector({
  6. super.key,
  7. this.child,
  8. this.onSingleTapUp,
  9. this.onTapUp,
  10. this.onTapDown,
  11. this.onSecondaryTapDown,
  12. this.onLongPressStart,
  13. this.onLongPressMoveUpdate,
  14. this.onLongPressUp,
  15. this.onDragStart,
  16. this.onDragUpdate,
  17. this.onDoubleTapDown,
  18. });
  19. final Widget? child;
  20. final GestureTapUpCallback? onTapUp;
  21. final GestureTapUpCallback? onSingleTapUp;
  22. final GestureTapDownCallback? onTapDown;
  23. final GestureTapDownCallback? onSecondaryTapDown;
  24. final GestureTapDownCallback? onDoubleTapDown;
  25. final GestureLongPressStartCallback? onLongPressStart;
  26. final GestureLongPressMoveUpdateCallback? onLongPressMoveUpdate;
  27. final GestureLongPressUpCallback? onLongPressUp;
  28. final GestureDragStartCallback? onDragStart;
  29. final GestureDragUpdateCallback? onDragUpdate;
  30. @override
  31. State<TerminalGestureDetector> createState() =>
  32. _TerminalGestureDetectorState();
  33. }
  34. class _TerminalGestureDetectorState extends State<TerminalGestureDetector> {
  35. Timer? _doubleTapTimer;
  36. Offset? _lastTapOffset;
  37. // True if a second tap down of a double tap is detected. Used to discard
  38. // subsequent tap up / tap hold of the same tap.
  39. bool _isDoubleTap = false;
  40. // The down handler is force-run on success of a single tap and optimistically
  41. // run before a long press success.
  42. void _handleTapDown(TapDownDetails details) {
  43. widget.onTapDown?.call(details);
  44. if (_doubleTapTimer != null &&
  45. _isWithinDoubleTapTolerance(details.globalPosition)) {
  46. // If there was already a previous tap, the second down hold/tap is a
  47. // double tap down.
  48. widget.onDoubleTapDown?.call(details);
  49. _doubleTapTimer!.cancel();
  50. _doubleTapTimeout();
  51. _isDoubleTap = true;
  52. }
  53. }
  54. void _handleTapUp(TapUpDetails details) {
  55. if (!_isDoubleTap) {
  56. widget.onSingleTapUp?.call(details);
  57. _lastTapOffset = details.globalPosition;
  58. _doubleTapTimer = Timer(kDoubleTapTimeout, _doubleTapTimeout);
  59. }
  60. _isDoubleTap = false;
  61. }
  62. void _doubleTapTimeout() {
  63. _doubleTapTimer = null;
  64. _lastTapOffset = null;
  65. }
  66. bool _isWithinDoubleTapTolerance(Offset secondTapOffset) {
  67. if (_lastTapOffset == null) {
  68. return false;
  69. }
  70. final Offset difference = secondTapOffset - _lastTapOffset!;
  71. return difference.distance <= kDoubleTapSlop;
  72. }
  73. @override
  74. Widget build(BuildContext context) {
  75. final gestures = <Type, GestureRecognizerFactory>{};
  76. gestures[TapGestureRecognizer] =
  77. GestureRecognizerFactoryWithHandlers<TapGestureRecognizer>(
  78. () => TapGestureRecognizer(debugOwner: this),
  79. (TapGestureRecognizer instance) {
  80. instance
  81. ..onTapDown = _handleTapDown
  82. ..onTapUp = _handleTapUp
  83. ..onSecondaryTapDown = widget.onSecondaryTapDown;
  84. },
  85. );
  86. gestures[LongPressGestureRecognizer] =
  87. GestureRecognizerFactoryWithHandlers<LongPressGestureRecognizer>(
  88. () => LongPressGestureRecognizer(
  89. debugOwner: this,
  90. supportedDevices: {
  91. PointerDeviceKind.touch,
  92. // PointerDeviceKind.mouse, // for debugging purposes only
  93. },
  94. ),
  95. (LongPressGestureRecognizer instance) {
  96. instance
  97. ..onLongPressStart = widget.onLongPressStart
  98. ..onLongPressMoveUpdate = widget.onLongPressMoveUpdate
  99. ..onLongPressUp = widget.onLongPressUp;
  100. },
  101. );
  102. gestures[PanGestureRecognizer] =
  103. GestureRecognizerFactoryWithHandlers<PanGestureRecognizer>(
  104. () => PanGestureRecognizer(
  105. debugOwner: this,
  106. supportedDevices: <PointerDeviceKind>{PointerDeviceKind.mouse},
  107. ),
  108. (PanGestureRecognizer instance) {
  109. instance
  110. ..dragStartBehavior = DragStartBehavior.down
  111. ..onStart = widget.onDragStart
  112. ..onUpdate = widget.onDragUpdate;
  113. },
  114. );
  115. return RawGestureDetector(
  116. gestures: gestures,
  117. excludeFromSemantics: true,
  118. child: widget.child,
  119. );
  120. }
  121. }