123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767 |
- import 'dart:async';
- import 'package:flutter/gestures.dart';
- import 'package:flutter/widgets.dart';
- enum GestureState {
- none,
- oneFingerPan,
- twoFingerScale,
- threeFingerVerticalDrag
- }
- class CustomTouchGestureRecognizer extends ScaleGestureRecognizer {
- CustomTouchGestureRecognizer({
- Object? debugOwner,
- Set<PointerDeviceKind>? supportedDevices,
- }) : super(
- debugOwner: debugOwner,
- supportedDevices: supportedDevices,
- ) {
- _init();
- }
- // oneFingerPan
- GestureDragStartCallback? onOneFingerPanStart;
- GestureDragUpdateCallback? onOneFingerPanUpdate;
- GestureDragEndCallback? onOneFingerPanEnd;
- // twoFingerScale : scale + pan event
- GestureScaleStartCallback? onTwoFingerScaleStart;
- GestureScaleUpdateCallback? onTwoFingerScaleUpdate;
- GestureScaleEndCallback? onTwoFingerScaleEnd;
- // threeFingerVerticalDrag
- GestureDragStartCallback? onThreeFingerVerticalDragStart;
- GestureDragUpdateCallback? onThreeFingerVerticalDragUpdate;
- GestureDragEndCallback? onThreeFingerVerticalDragEnd;
- var _currentState = GestureState.none;
- Timer? _debounceTimer;
- void _init() {
- debugPrint("CustomTouchGestureRecognizer init");
- // onStart = (d) {};
- onUpdate = (d) {
- _debounceTimer?.cancel();
- if (d.pointerCount == 1 && _currentState != GestureState.oneFingerPan) {
- onOneFingerStartDebounce(d);
- } else if (d.pointerCount == 2 &&
- _currentState != GestureState.twoFingerScale) {
- onTwoFingerStartDebounce(d);
- } else if (d.pointerCount == 3 &&
- _currentState != GestureState.threeFingerVerticalDrag) {
- _currentState = GestureState.threeFingerVerticalDrag;
- if (onThreeFingerVerticalDragStart != null) {
- onThreeFingerVerticalDragStart!(
- DragStartDetails(globalPosition: d.localFocalPoint));
- }
- debugPrint("start threeFingerScale");
- }
- if (_currentState != GestureState.none) {
- switch (_currentState) {
- case GestureState.oneFingerPan:
- if (onOneFingerPanUpdate != null) {
- onOneFingerPanUpdate!(_getDragUpdateDetails(d));
- }
- break;
- case GestureState.twoFingerScale:
- if (onTwoFingerScaleUpdate != null) {
- onTwoFingerScaleUpdate!(d);
- }
- break;
- case GestureState.threeFingerVerticalDrag:
- if (onThreeFingerVerticalDragUpdate != null) {
- onThreeFingerVerticalDragUpdate!(_getDragUpdateDetails(d));
- }
- break;
- default:
- break;
- }
- return;
- }
- };
- onEnd = (d) {
- debugPrint("ScaleGestureRecognizer onEnd");
- _debounceTimer?.cancel();
- // end
- switch (_currentState) {
- case GestureState.oneFingerPan:
- debugPrint("OneFingerState.pan onEnd");
- if (onOneFingerPanEnd != null) {
- onOneFingerPanEnd!(_getDragEndDetails(d));
- }
- break;
- case GestureState.twoFingerScale:
- debugPrint("TwoFingerState.scale onEnd");
- if (onTwoFingerScaleEnd != null) {
- onTwoFingerScaleEnd!(d);
- }
- break;
- case GestureState.threeFingerVerticalDrag:
- debugPrint("ThreeFingerState.vertical onEnd");
- if (onThreeFingerVerticalDragEnd != null) {
- onThreeFingerVerticalDragEnd!(_getDragEndDetails(d));
- }
- break;
- default:
- break;
- }
- _debounceTimer = Timer(Duration(milliseconds: 200), () {
- _currentState = GestureState.none;
- });
- };
- }
- // FIXME: This debounce logic is not working properly.
- // If we move our finger very fast, we won't be able to detect the "oneFingerPan" event sometimes.
- void onOneFingerStartDebounce(ScaleUpdateDetails d) {
- start(ScaleUpdateDetails d) {
- _currentState = GestureState.oneFingerPan;
- if (onOneFingerPanStart != null) {
- onOneFingerPanStart!(DragStartDetails(
- localPosition: d.localFocalPoint, globalPosition: d.focalPoint));
- }
- }
- if (_currentState != GestureState.none) {
- _debounceTimer = Timer(Duration(milliseconds: 200), () {
- start(d);
- debugPrint("debounce start oneFingerPan");
- });
- } else {
- start(d);
- debugPrint("start oneFingerPan");
- }
- }
- void onTwoFingerStartDebounce(ScaleUpdateDetails d) {
- start(ScaleUpdateDetails d) {
- _currentState = GestureState.twoFingerScale;
- if (onTwoFingerScaleStart != null) {
- onTwoFingerScaleStart!(ScaleStartDetails(
- localFocalPoint: d.localFocalPoint, focalPoint: d.focalPoint));
- }
- }
- if (_currentState == GestureState.threeFingerVerticalDrag) {
- _debounceTimer = Timer(Duration(milliseconds: 200), () {
- start(d);
- debugPrint("debounce start twoFingerScale");
- });
- } else {
- start(d);
- debugPrint("start twoFingerScale");
- }
- }
- DragUpdateDetails _getDragUpdateDetails(ScaleUpdateDetails d) =>
- DragUpdateDetails(
- globalPosition: d.focalPoint,
- localPosition: d.localFocalPoint,
- delta: d.focalPointDelta);
- DragEndDetails _getDragEndDetails(ScaleEndDetails d) =>
- DragEndDetails(velocity: d.velocity);
- }
- class HoldTapMoveGestureRecognizer extends GestureRecognizer {
- HoldTapMoveGestureRecognizer({
- Object? debugOwner,
- Set<PointerDeviceKind>? supportedDevices,
- }) : super(
- debugOwner: debugOwner,
- supportedDevices: supportedDevices,
- );
- GestureDragStartCallback? onHoldDragStart;
- GestureDragUpdateCallback? onHoldDragUpdate;
- GestureDragDownCallback? onHoldDragDown;
- GestureDragCancelCallback? onHoldDragCancel;
- GestureDragEndCallback? onHoldDragEnd;
- bool _isStart = false;
- Timer? _firstTapUpTimer;
- Timer? _secondTapDownTimer;
- _TapTracker? _firstTap;
- _TapTracker? _secondTap;
- PointerDownEvent? _lastPointerDownEvent;
- final Map<int, _TapTracker> _trackers = <int, _TapTracker>{};
- @override
- bool isPointerAllowed(PointerDownEvent event) {
- if (_firstTap == null) {
- switch (event.buttons) {
- case kPrimaryButton:
- if (onHoldDragStart == null &&
- onHoldDragUpdate == null &&
- onHoldDragCancel == null &&
- onHoldDragEnd == null) {
- return false;
- }
- break;
- default:
- return false;
- }
- }
- return super.isPointerAllowed(event);
- }
- @override
- void addAllowedPointer(PointerDownEvent event) {
- if (_firstTap != null) {
- if (!_firstTap!.isWithinGlobalTolerance(event, kDoubleTapSlop)) {
- // Ignore out-of-bounds second taps.
- return;
- } else if (!_firstTap!.hasElapsedMinTime() ||
- !_firstTap!.hasSameButton(event)) {
- // Restart when the second tap is too close to the first (touch screens
- // often detect touches intermittently), or when buttons mismatch.
- _reset();
- return _trackTap(event);
- } else if (onHoldDragDown != null) {
- invokeCallback<void>(
- 'onHoldDragDown',
- () => onHoldDragDown!(DragDownDetails(
- globalPosition: event.position,
- localPosition: event.localPosition)));
- }
- }
- _trackTap(event);
- }
- void _trackTap(PointerDownEvent event) {
- _stopFirstTapUpTimer();
- _stopSecondTapDownTimer();
- final _TapTracker tracker = _TapTracker(
- event: event,
- entry: GestureBinding.instance.gestureArena.add(event.pointer, this),
- doubleTapMinTime: kDoubleTapMinTime,
- gestureSettings: gestureSettings,
- );
- _trackers[event.pointer] = tracker;
- _lastPointerDownEvent = event;
- tracker.startTrackingPointer(_handleEvent, event.transform);
- }
- void _handleEvent(PointerEvent event) {
- final _TapTracker tracker = _trackers[event.pointer]!;
- if (event is PointerUpEvent) {
- if (_firstTap == null && _secondTap == null) {
- _registerFirstTap(tracker);
- } else if (_secondTap != null) {
- if (event.pointer == _secondTap!.pointer) {
- if (onHoldDragEnd != null) {
- onHoldDragEnd!(DragEndDetails());
- _secondTap = null;
- _isStart = false;
- }
- }
- } else {
- _reject(tracker);
- }
- } else if (event is PointerDownEvent) {
- if (_firstTap != null && _secondTap == null) {
- _registerSecondTap(tracker);
- }
- } else if (event is PointerMoveEvent) {
- if (!tracker.isWithinGlobalTolerance(event, kDoubleTapTouchSlop)) {
- if (_firstTap != null && _firstTap!.pointer == event.pointer) {
- // first tap move
- _reject(tracker);
- } else if (_secondTap != null && _secondTap!.pointer == event.pointer) {
- // debugPrint("_secondTap move");
- // second tap move
- if (!_isStart) {
- _resolve();
- }
- if (onHoldDragUpdate != null) {
- onHoldDragUpdate!(DragUpdateDetails(
- globalPosition: event.position,
- localPosition: event.localPosition,
- delta: event.delta));
- }
- }
- }
- } else if (event is PointerCancelEvent) {
- _reject(tracker);
- }
- }
- @override
- void acceptGesture(int pointer) {}
- @override
- void rejectGesture(int pointer) {
- _TapTracker? tracker = _trackers[pointer];
- // If tracker isn't in the list, check if this is the first tap tracker
- if (tracker == null && _firstTap != null && _firstTap!.pointer == pointer) {
- tracker = _firstTap;
- }
- // If tracker is still null, we rejected ourselves already
- if (tracker != null) {
- _reject(tracker);
- }
- }
- void _resolve() {
- _stopSecondTapDownTimer();
- _firstTap?.entry.resolve(GestureDisposition.accepted);
- _secondTap?.entry.resolve(GestureDisposition.accepted);
- _isStart = true;
- // TODO start details
- if (onHoldDragStart != null) {
- onHoldDragStart!(DragStartDetails(
- kind: _lastPointerDownEvent?.kind,
- ));
- }
- }
- void _reject(_TapTracker tracker) {
- try {
- _checkCancel();
- _isStart = false;
- _trackers.remove(tracker.pointer);
- tracker.entry.resolve(GestureDisposition.rejected);
- _freezeTracker(tracker);
- _reset();
- } catch (e) {
- debugPrint("Failed to _reject:$e");
- }
- }
- @override
- void dispose() {
- _reset();
- super.dispose();
- }
- void _reset() {
- _isStart = false;
- // debugPrint("reset");
- _stopFirstTapUpTimer();
- _stopSecondTapDownTimer();
- if (_firstTap != null) {
- if (_trackers.isNotEmpty) {
- _checkCancel();
- }
- // Note, order is important below in order for the resolve -> reject logic
- // to work properly.
- final _TapTracker tracker = _firstTap!;
- _firstTap = null;
- _reject(tracker);
- GestureBinding.instance.gestureArena.release(tracker.pointer);
- if (_secondTap != null) {
- final _TapTracker tracker = _secondTap!;
- _secondTap = null;
- _reject(tracker);
- GestureBinding.instance.gestureArena.release(tracker.pointer);
- }
- }
- _firstTap = null;
- _secondTap = null;
- _clearTrackers();
- }
- void _registerFirstTap(_TapTracker tracker) {
- _startFirstTapUpTimer();
- GestureBinding.instance.gestureArena.hold(tracker.pointer);
- // Note, order is important below in order for the clear -> reject logic to
- // work properly.
- _freezeTracker(tracker);
- _trackers.remove(tracker.pointer);
- _firstTap = tracker;
- }
- void _registerSecondTap(_TapTracker tracker) {
- if (_firstTap != null) {
- _stopFirstTapUpTimer();
- _freezeTracker(_firstTap!);
- _firstTap = null;
- }
- _startSecondTapDownTimer();
- GestureBinding.instance.gestureArena.hold(tracker.pointer);
- _secondTap = tracker;
- // TODO
- }
- void _clearTrackers() {
- _trackers.values.toList().forEach(_reject);
- assert(_trackers.isEmpty);
- }
- void _freezeTracker(_TapTracker tracker) {
- tracker.stopTrackingPointer(_handleEvent);
- }
- void _startFirstTapUpTimer() {
- _firstTapUpTimer ??= Timer(kDoubleTapTimeout, _reset);
- }
- void _startSecondTapDownTimer() {
- _secondTapDownTimer ??= Timer(kDoubleTapTimeout, _resolve);
- }
- void _stopFirstTapUpTimer() {
- if (_firstTapUpTimer != null) {
- _firstTapUpTimer!.cancel();
- _firstTapUpTimer = null;
- }
- }
- void _stopSecondTapDownTimer() {
- if (_secondTapDownTimer != null) {
- _secondTapDownTimer!.cancel();
- _secondTapDownTimer = null;
- }
- }
- void _checkCancel() {
- if (onHoldDragCancel != null) {
- invokeCallback<void>('onHoldDragCancel', onHoldDragCancel!);
- }
- }
- @override
- String get debugDescription => 'double tap';
- }
- class DoubleFinerTapGestureRecognizer extends GestureRecognizer {
- DoubleFinerTapGestureRecognizer({
- Object? debugOwner,
- Set<PointerDeviceKind>? supportedDevices,
- }) : super(
- debugOwner: debugOwner,
- supportedDevices: supportedDevices,
- );
- GestureTapDownCallback? onDoubleFinerTapDown;
- GestureTapDownCallback? onDoubleFinerTap;
- GestureTapCancelCallback? onDoubleFinerTapCancel;
- Timer? _firstTapTimer;
- _TapTracker? _firstTap;
- PointerDownEvent? _lastPointerDownEvent;
- var _isStart = false;
- final Set<int> _upTap = {};
- final Map<int, _TapTracker> _trackers = <int, _TapTracker>{};
- @override
- bool isPointerAllowed(PointerDownEvent event) {
- if (_firstTap == null) {
- switch (event.buttons) {
- case kPrimaryButton:
- if (onDoubleFinerTapDown == null &&
- onDoubleFinerTap == null &&
- onDoubleFinerTapCancel == null) {
- return false;
- }
- break;
- default:
- return false;
- }
- }
- return super.isPointerAllowed(event);
- }
- @override
- void addAllowedPointer(PointerDownEvent event) {
- debugPrint("addAllowedPointer");
- if (_isStart) {
- // second
- if (onDoubleFinerTapDown != null) {
- final TapDownDetails details = TapDownDetails(
- globalPosition: event.position,
- localPosition: event.localPosition,
- kind: getKindForPointer(event.pointer),
- );
- invokeCallback<void>(
- 'onDoubleFinerTapDown', () => onDoubleFinerTapDown!(details));
- }
- } else {
- // first tap
- _isStart = true;
- _lastPointerDownEvent = event;
- _startFirstTapDownTimer();
- }
- _trackTap(event);
- }
- void _trackTap(PointerDownEvent event) {
- final _TapTracker tracker = _TapTracker(
- event: event,
- entry: GestureBinding.instance.gestureArena.add(event.pointer, this),
- doubleTapMinTime: kDoubleTapMinTime,
- gestureSettings: gestureSettings,
- );
- _trackers[event.pointer] = tracker;
- // debugPrint("_trackers:$_trackers");
- tracker.startTrackingPointer(_handleEvent, event.transform);
- _registerTap(tracker);
- }
- void _handleEvent(PointerEvent event) {
- final _TapTracker tracker = _trackers[event.pointer]!;
- if (event is PointerUpEvent) {
- debugPrint("PointerUpEvent");
- _upTap.add(tracker.pointer);
- } else if (event is PointerMoveEvent) {
- if (!tracker.isWithinGlobalTolerance(event, kDoubleTapTouchSlop)) {
- _reject(tracker);
- }
- } else if (event is PointerCancelEvent) {
- _reject(tracker);
- }
- }
- @override
- void acceptGesture(int pointer) {}
- @override
- void rejectGesture(int pointer) {
- _TapTracker? tracker = _trackers[pointer];
- // If tracker isn't in the list, check if this is the first tap tracker
- if (tracker == null && _firstTap != null && _firstTap!.pointer == pointer) {
- tracker = _firstTap;
- }
- // If tracker is still null, we rejected ourselves already
- if (tracker != null) {
- _reject(tracker);
- }
- }
- void _reject(_TapTracker tracker) {
- _trackers.remove(tracker.pointer);
- tracker.entry.resolve(GestureDisposition.rejected);
- _freezeTracker(tracker);
- if (_firstTap != null) {
- if (tracker == _firstTap) {
- _reset();
- } else {
- _checkCancel();
- if (_trackers.isEmpty) {
- _reset();
- }
- }
- }
- }
- @override
- void dispose() {
- _reset();
- super.dispose();
- }
- void _reset() {
- _stopFirstTapUpTimer();
- _firstTap = null;
- _clearTrackers();
- }
- void _registerTap(_TapTracker tracker) {
- GestureBinding.instance.gestureArena.hold(tracker.pointer);
- // Note, order is important below in order for the clear -> reject logic to
- // work properly.
- }
- void _clearTrackers() {
- _trackers.values.toList().forEach(_reject);
- assert(_trackers.isEmpty);
- }
- void _freezeTracker(_TapTracker tracker) {
- tracker.stopTrackingPointer(_handleEvent);
- }
- void _startFirstTapDownTimer() {
- _firstTapTimer ??= Timer(kDoubleTapTimeout, _timeoutCheck);
- }
- void _stopFirstTapUpTimer() {
- if (_firstTapTimer != null) {
- _firstTapTimer!.cancel();
- _firstTapTimer = null;
- }
- }
- void _timeoutCheck() {
- _isStart = false;
- if (_upTap.length == 2) {
- _resolve();
- } else {
- _reset();
- }
- _upTap.clear();
- }
- void _resolve() {
- // TODO tap down details
- if (onDoubleFinerTap != null) {
- onDoubleFinerTap!(TapDownDetails(
- kind: _lastPointerDownEvent?.kind,
- ));
- }
- _trackers.forEach((key, value) {
- value.entry.resolve(GestureDisposition.accepted);
- });
- _reset();
- }
- void _checkCancel() {
- if (onDoubleFinerTapCancel != null) {
- invokeCallback<void>('onHoldDragCancel', onDoubleFinerTapCancel!);
- }
- }
- @override
- String get debugDescription => 'double tap';
- }
- /// TapTracker helps track individual tap sequences as part of a
- /// larger gesture.
- class _TapTracker {
- _TapTracker({
- required PointerDownEvent event,
- required this.entry,
- required Duration doubleTapMinTime,
- required this.gestureSettings,
- }) : pointer = event.pointer,
- _initialGlobalPosition = event.position,
- initialButtons = event.buttons,
- _doubleTapMinTimeCountdown =
- _CountdownZoned(duration: doubleTapMinTime);
- final DeviceGestureSettings? gestureSettings;
- final int pointer;
- final GestureArenaEntry entry;
- final Offset _initialGlobalPosition;
- final int initialButtons;
- final _CountdownZoned _doubleTapMinTimeCountdown;
- bool _isTrackingPointer = false;
- void startTrackingPointer(PointerRoute route, Matrix4? transform) {
- if (!_isTrackingPointer) {
- _isTrackingPointer = true;
- GestureBinding.instance.pointerRouter.addRoute(pointer, route, transform);
- }
- }
- void stopTrackingPointer(PointerRoute route) {
- if (_isTrackingPointer) {
- _isTrackingPointer = false;
- GestureBinding.instance.pointerRouter.removeRoute(pointer, route);
- }
- }
- bool isWithinGlobalTolerance(PointerEvent event, double tolerance) {
- final Offset offset = event.position - _initialGlobalPosition;
- return offset.distance <= tolerance;
- }
- bool hasElapsedMinTime() {
- return _doubleTapMinTimeCountdown.timeout;
- }
- bool hasSameButton(PointerDownEvent event) {
- return event.buttons == initialButtons;
- }
- }
- /// CountdownZoned tracks whether the specified duration has elapsed since
- /// creation, honoring [Zone].
- class _CountdownZoned {
- _CountdownZoned({required Duration duration}) {
- Timer(duration, _onTimeout);
- }
- bool _timeout = false;
- bool get timeout => _timeout;
- void _onTimeout() {
- _timeout = true;
- }
- }
- RawGestureDetector getMixinGestureDetector({
- Widget? child,
- GestureTapUpCallback? onTapUp,
- GestureTapDownCallback? onDoubleTapDown,
- GestureDoubleTapCallback? onDoubleTap,
- GestureLongPressDownCallback? onLongPressDown,
- GestureLongPressCallback? onLongPress,
- GestureDragStartCallback? onHoldDragStart,
- GestureDragUpdateCallback? onHoldDragUpdate,
- GestureDragCancelCallback? onHoldDragCancel,
- GestureDragEndCallback? onHoldDragEnd,
- GestureTapDownCallback? onDoubleFinerTap,
- GestureDragStartCallback? onOneFingerPanStart,
- GestureDragUpdateCallback? onOneFingerPanUpdate,
- GestureDragEndCallback? onOneFingerPanEnd,
- GestureScaleUpdateCallback? onTwoFingerScaleUpdate,
- GestureScaleEndCallback? onTwoFingerScaleEnd,
- GestureDragUpdateCallback? onThreeFingerVerticalDragUpdate,
- }) {
- return RawGestureDetector(
- child: child,
- gestures: <Type, GestureRecognizerFactory>{
- // Official
- TapGestureRecognizer:
- GestureRecognizerFactoryWithHandlers<TapGestureRecognizer>(
- () => TapGestureRecognizer(), (instance) {
- instance.onTapUp = onTapUp;
- }),
- DoubleTapGestureRecognizer:
- GestureRecognizerFactoryWithHandlers<DoubleTapGestureRecognizer>(
- () => DoubleTapGestureRecognizer(), (instance) {
- instance
- ..onDoubleTapDown = onDoubleTapDown
- ..onDoubleTap = onDoubleTap;
- }),
- LongPressGestureRecognizer:
- GestureRecognizerFactoryWithHandlers<LongPressGestureRecognizer>(
- () => LongPressGestureRecognizer(), (instance) {
- instance
- ..onLongPressDown = onLongPressDown
- ..onLongPress = onLongPress;
- }),
- // Customized
- HoldTapMoveGestureRecognizer:
- GestureRecognizerFactoryWithHandlers<HoldTapMoveGestureRecognizer>(
- () => HoldTapMoveGestureRecognizer(),
- (instance) => instance
- ..onHoldDragStart = onHoldDragStart
- ..onHoldDragUpdate = onHoldDragUpdate
- ..onHoldDragCancel = onHoldDragCancel
- ..onHoldDragEnd = onHoldDragEnd),
- DoubleFinerTapGestureRecognizer: GestureRecognizerFactoryWithHandlers<
- DoubleFinerTapGestureRecognizer>(
- () => DoubleFinerTapGestureRecognizer(), (instance) {
- instance.onDoubleFinerTap = onDoubleFinerTap;
- }),
- CustomTouchGestureRecognizer:
- GestureRecognizerFactoryWithHandlers<CustomTouchGestureRecognizer>(
- () => CustomTouchGestureRecognizer(), (instance) {
- instance
- ..onOneFingerPanStart = onOneFingerPanStart
- ..onOneFingerPanUpdate = onOneFingerPanUpdate
- ..onOneFingerPanEnd = onOneFingerPanEnd
- ..onTwoFingerScaleUpdate = onTwoFingerScaleUpdate
- ..onTwoFingerScaleEnd = onTwoFingerScaleEnd
- ..onThreeFingerVerticalDragUpdate = onThreeFingerVerticalDragUpdate;
- }),
- });
- }
|