Flutter: Using both Pan and Scale gestures together?
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.
Admin
Updated on December 20, 2022Comments
-
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) {} }