Flutter: Using both Pan and Scale gestures together?

2,503

Solution 1

What you want to do is to replace onPanUpdate, onPanEnd... with onScaleUpdate, onScaleEnd.... Then just instead of details.globalPosition use details.focalPoint. You will get the same behaviour as Pan, plus you will now have access to details.scale or details.rotation. Here is an example.

Before:

onPanUpdate: (DragUpdateDetails details){
              newPosition = details.globalPosition;
              //scale and rotation missing in pan
            },

After:

onScaleUpdate: (ScaleUpdateDetails details){
              newPosition = details.focalPoint;
              newRotation = details.rotation;
              newScale = details.scale;
              //scale and rotation now available
            },

Hope this helps. Took me some time to figure it out too.

Solution 2

Using Listener widget for listen pointer event, then handle pointer event to detect scale and pan gesture as you need.

  • 1 pointer (down and up in short interval time) => Tap
  • 1 pointer (down and up in long interval time) => Double Tap
  • 1 pointer (down and moving) => Pan
  • 2 pointer => scale and rotate

you can using gesture_x_detector for full support gestures(tap, double tap, scale, rotate, long press,..) together.

Share:
2,503
Admin
Author by

Admin

Updated on December 20, 2022

Comments

  • Admin
    Admin over 1 year

    I am creating a drawing canvas with flutter for which I want to use both scale (for zooming, panning and rotating) and Pan (for drawing with fingers) gestures but flutter doesn't allow that since Scale is a superset of Pan. Is there any workaround for this?

    I tried to create my own custom gesture recognizer for detecting the number of pointers on the screen so that if two pointers come in contact with the screen within a short interval of time(let's say 1 second) Scale takes over otherwise pan starts working with the 1st pointer that touched the screen. I am using matrix_gesture_detector package for doing the scaling part.

    Here is my code for the custom gesture recognizer

    class ScaleAndPanRecognizer extends OneSequenceGestureRecognizer {
      var manager = GestureArenaManager();
      final Function onPanDown;
      final Function onPanUpdate;
      final Function onPanEnd;
      ScaleAndPanRecognizer({
        @required this.onPanDown,
        @required this.onPanUpdate,
        @required this.onPanEnd,
      });
    
      int numPointers = 0;
      int timeFrame = 1;
      int pointer1;
      int pointer2;
      var time1;
      var time2;
      var position1;
    
      @override
      void addPointer(PointerDownEvent event) {
        print(numPointers);
        if (numPointers == 0) {
          pointer1 = event.pointer;
          print(pointer1);
          position1 = event.localPosition;
          manager.hold(pointer1); //hold the assignment for this pointer for now
          startTrackingPointer(pointer1);
    
          numPointers += 1;
          time1 = DateTime.now();
        } else if (numPointers == 1) {
          pointer2 = event.pointer;
          print(pointer2);
          resolvePointer(pointer2, GestureDisposition.rejected);
          time2 = DateTime.now();
          var diff = time2.difference(time1);
          print(diff);
          if (diff <= Duration(seconds: timeFrame)) {
            manager.release(pointer1);
            resolvePointer(pointer1, GestureDisposition.rejected);
          } else {
            resolvePointer(pointer1, GestureDisposition.accepted);
            manager.release(pointer1);
            onPanDown(position1);
          }
          numPointers = 0;
        }
      }
    
      @override
      void handleEvent(PointerEvent event) {
        if (event is PointerMoveEvent) {
          onPanUpdate(event.localPosition);
        }
        if (event is PointerUpEvent) {
          onPanEnd();
          stopTrackingPointer(event.pointer);
        }
      }
    
      @override
      String get debugDescription => 'only one pointer recognizer';
    
      @override
      void didStopTrackingLastPointer(int pointer) {}
    
    }