Luke Hutchison
Luke Hutchison

Reputation: 9220

Flutter `GestureRecognizer` that only responds to two-finger gestures

How do I create a GestureRecognizer in Flutter that only responds to two-finger gestures, and passes single-finger gestures on to other widgets in the arena?

In my Flutter app, I have a GoogleMap widget inside a SingleChildScrollView. The GoogleMap widget takes a gestureRecognizers parameter which I have set to the following, otherwise it is not even possible to interact with the map (the ScrollView gets all the touch events, at least 95% of the time...)

  gestureRecognizers: const {
    Factory<OneSequenceGestureRecognizer>(
      EagerGestureRecognizer.new,
    ),
  },

With this GestureRecognizer, the Google Maps widget responds to single-finger gestures (pan) and two-finger gestures (zoom/pan). I want the widget to respond only two-finger gestures, because currently dragging with one finger on the map widget interrupts the user's ability to scroll the view up or down. So I want single-finger gestures to go to the ScrollView, and two-finger gestures to go to Google Maps. (The Google Maps web widget requires a two-finger gesture for pan, for the same reason...)

How do I replace EagerGestureRecognizer with a gesture recognizer that causes Maps to only pan if two fingers are down, otherwise to pass the single-finger gesture through to the scroll view?

Upvotes: 0

Views: 393

Answers (2)

Aks
Aks

Reputation: 1671

Is this the correct way to use this?

Currently, I am using it like this but the working of single finger pan and two finger pan is still the same.

 gestureRecognizers: {
                    Factory<OneSequenceGestureRecognizer>(
                      () => TwoFingerPanZoomEagerGestureRecognizer(),
                    ),
                  },

Upvotes: 0

Luke Hutchison
Luke Hutchison

Reputation: 9220

OK, I figured it out. The trick is that you need to use a GestureAreaManager to hold the first touch point until the second touch point goes down:

class TwoFingerPanZoomEagerGestureRecognizer extends EagerGestureRecognizer {
  final _gestureArenaManager = GestureArenaManager();
  int? _firstPointer;
  int? _secondPointer;

  @override
  void addAllowedPointer(PointerDownEvent event) {
    startTrackingPointer(event.pointer);
    if (_firstPointer == null) {
      // If 1 touchpoint is down, hold the pointer until the 2nd touchpoint
      // is down
      _firstPointer = event.pointer;
      _gestureArenaManager.hold(event.pointer);
    } else if (_secondPointer == null) {
      // If 2 touchpoints are down, release the first pointer, and accept
      // both touchpoints
      _gestureArenaManager.release(_firstPointer!);
      resolvePointer(_firstPointer!, GestureDisposition.accepted);
      _secondPointer = event.pointer;
      resolvePointer(_secondPointer!, GestureDisposition.accepted);
    } else {
      // If 3rd touchpoint is down, ignore it
      resolvePointer(event.pointer, GestureDisposition.rejected);
    }
  }

  @override
  void handleEvent(PointerEvent event) {
    if (event is PointerUpEvent) {
      stopTrackingPointer(event.pointer);
      if (_secondPointer == event.pointer) {
        // Second pointer was lifted
        stopTrackingPointer(_secondPointer!);
        _secondPointer = null;
      } else if (_firstPointer == event.pointer) {
        // First pointer was lifted
        stopTrackingPointer(_firstPointer!);
        _gestureArenaManager.release(_firstPointer!);
        // Move second pointer to first pointer slot
        _firstPointer = _secondPointer;
        _secondPointer = null;
      }
    } else {
      super.handleEvent(event);
    }
  }
}

Upvotes: 0

Related Questions