Flutter Zoomable Widget
Solution 1
As of Flutter 1.20, InteractiveViewer widget supports pan and Zoom out of the box.
To make any widget zoomable you need to simply wrap the child with InteractiveViewer
.
@override
Widget build(BuildContext context) {
return Center(
child: InteractiveViewer(
panEnabled: false, // Set it to false to prevent panning.
boundaryMargin: EdgeInsets.all(80),
minScale: 0.5,
maxScale: 4,
child: FlutterLogo(size: 200),
),
);
}
Solution 2
This is working perfectly now, thanks for the reference @pskink.
import 'package:flutter/material.dart';
import 'package:matrix_gesture_detector/matrix_gesture_detector.dart';
class ZoomableWidget extends StatefulWidget {
final Widget child;
const ZoomableWidget({Key key, this.child}) : super(key: key);
@override
_ZoomableWidgetState createState() => _ZoomableWidgetState();
}
class _ZoomableWidgetState extends State<ZoomableWidget> {
Matrix4 matrix = Matrix4.identity();
@override
Widget build(BuildContext context) {
return MatrixGestureDetector(
onMatrixUpdate: (Matrix4 m, Matrix4 tm, Matrix4 sm, Matrix4 rm) {
setState(() {
matrix = m;
});
},
child: Transform(
transform: matrix,
child: widget.child,
),
);
}
}
Solution 3
I loved de resolution, you should put that in a packged in pub, you can even put some custom options, in my code I put doubletap to reset the zoom and locked the rotation.
import 'package:flutter/material.dart';
import 'package:matrix_gesture_detector/matrix_gesture_detector.dart';
class ZoomableWidget extends StatefulWidget {
final Widget child;
const ZoomableWidget({Key key, this.child}) : super(key: key);
@override
_ZoomableWidgetState createState() => _ZoomableWidgetState();
}
class _ZoomableWidgetState extends State<ZoomableWidget> {
Matrix4 matrix = Matrix4.identity();
Matrix4 zerada = Matrix4.identity();
@override
Widget build(BuildContext context) {
return GestureDetector(
onDoubleTap: (){
setState(() {
matrix = zerada;
});
},
child: MatrixGestureDetector(
shouldRotate: false,
onMatrixUpdate: (Matrix4 m, Matrix4 tm, Matrix4 sm, Matrix4 rm) {
setState(() {
matrix = m;
});
},
child: Transform(
transform: matrix,
child: widget.child,
),
),
);
}
}
Solution 4
As an alternative to MatrixGestureDetector, you can use the photo_view package: https://pub.dev/packages/photo_view
It has good limiting of the screen constraints so you can't drag the child off-screen, a bounce effect when hitting min/max size, and many other options.
It can be used with a custom child like this:
PhotoView.customChild(
child: <your widget>
)
Solution 5
You can use Zoom Widget Zoom Widget only need set a canvas size and child
Zoom(
width: 1800,
height: 1800,
child: Center(
child: Text("Happy zoom!!"),
)
);
Aawaz Gyawali
Android developer with knowledge of MySQL and php.
Updated on December 30, 2021Comments
-
Aawaz Gyawali over 2 years
What I want to build is a widget that can make its child widget zoomable similar to the zoomable behavior.
Gestures I want to cover are
- Pinch To Zoom
- Double Tap to Zoom
- Tap to get the local Position of the widget
Here is my widget plan:
ZoomableWidget( child: // My custom Widget which should be zoomable. )
Here is my current progress:
import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:vector_math/vector_math_64.dart'; class ZoomableWidget extends StatefulWidget { final Widget child; const ZoomableWidget({Key key, this.child}) : super(key: key); @override _ZoomableWidgetState createState() => _ZoomableWidgetState(); } class _ZoomableWidgetState extends State<ZoomableWidget> { double _scale = 1.0; double _previousScale; @override Widget build(BuildContext context) { return ClipRect( child: GestureDetector( onScaleStart: (ScaleStartDetails details) { _previousScale = _scale; }, onScaleUpdate: (ScaleUpdateDetails details) { setState(() { _scale = _previousScale * details.scale; }); }, onScaleEnd: (ScaleEndDetails details) { _previousScale = null; }, child: Transform( transform: Matrix4.diagonal3(Vector3(_scale.clamp(1.0, 5.0), _scale.clamp(1.0, 5.0), _scale.clamp(1.0, 5.0))), alignment: FractionalOffset.center, child: widget.child, ), ), ); } }
The problem I have faced is, I cannot change the center of the pinch thus the image only zooms at (0,0) even after I zoom in the corner. Also, I cannot access horizontal drag and vertical drag to scroll the widget.
Thanks in advance.
-
autlunatic over 4 yearsI like this solution, the one thing i don't like is, that when you zoom and doubletap and zoom again. the zooming seems to start randomly anywhere. When you use a Key, wich is newly generated on doubleTap, it works better for me. Please let me know, if there is a better solution.
-
Evripides Kyriacou almost 4 yearsWhy is so hard to zoom I have a list with images that displaying using carouse, I used the above and is very hard to zoom, when I am trying to zoom the app is trying to scroll down , if I placed my fingers for a few seconds then is working fine
-
Jose Georges almost 4 yearscan you explain your solution? @autlunatic, I'm not sure which widget should get the key
-
autlunatic almost 4 yearsi cannot remember exactly. Maybe i mixed something up. i have a version of this where doubletap reverts to "default Zoom". There was a problem that zooming after Doubletap startet at a "wrong" point because it had saved the Matrix or something else. To Solve this i generated a new gestureKey for the Gesturedetector on doubletap, wich worked fine for me.
-
kakyo over 3 yearsDoesn't seem to be able to scale down html page inside a
WebView
, only up-scaling works. -
Mrak Vladar over 3 yearsHow can you make it to restore to default after the interaction is over. Take Interacting with an Image for example.
-
Till Friebe over 3 years@MrakVladar You can restore to the default by assigning a
TransformationController
to theInteractiveViewer
and set the value of the controller toMatrix4.identity()
. -
Noor about 3 yearsHow to make it work smoothly with a listview inside? Sometimes have to press it really hard to zoom in/out.
-
Alpit Panchal about 3 yearsit's helpful full but I used InteractiveViewer with GestureDetector by changing the position of the multiple widgets that time it's not working perfectly because of InteractiveViewer only works according to the child widget size. So I can't give inside the Stack widget every child full size. what we do for this issue. Please help and Thanks
-
litt over 2 yearsHey @autlunatic, I'm facing the same issue. Can you give a code snippet on how you generated the key for the GestureDetector to solve this issue? It'd be great if you could do this. Thanks in advance.
-
autlunatic over 2 years@RCN look at Jilly Tabogas next answer. I added a key which i created new on doubletap. GestureDetector( key: _gestureKey, onDoubleTap: () { setState(() { _gestureKey = GlobalKey(); matrix = zerada; }); }, child: MatrixGestureDetector( shouldRotate: false, onMatrixUpdate: (m, tm, sm, rm) { setState(() { matrix = m; }); }, child: Transform( transform: matrix, child: widget.child, ), ), ); }
-
Oliver Dixon about 2 yearsDoesn't work on MacOS, just errors with
'package:flutter/src/rendering/mouse_tracker.dart': Failed assertion: line 201 pos 12: '!_debugDuringDeviceUpdate': is not true.