skip to Main Content

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?

2

Answers


  1. Chosen as BEST ANSWER

    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);
        }
      }
    }
    

  2. 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(),
                        ),
                      },
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search