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) {}
}
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.
Using Listener widget for listen pointer event, then handle pointer event to detect scale and pan gesture as you need.
you can using gesture_x_detector for full support gestures(tap, double tap, scale, rotate, long press,..) together.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With