This tutorial shows you how to use InteractiveViewer
widget in Flutter.
InteractiveViewer
is a widget that allows pan and zoom interactions with its child. One of the common use cases of the widget is for displaying an image where the user can perform zoom (pinch-to-zoom) and pan gestures on the image. In this tutorial, I am going to show you how to use Flutter's InteractiveViewer
widget.
Using InteractiveViewer
The constructor of InteractiveViewer
is as follows.
InteractiveViewer({
Key key,
this.alignPanAxis = false,
this.boundaryMargin = EdgeInsets.zero,
this.constrained = true,
this.maxScale = 2.5,
this.minScale = 0.8,
this.onInteractionEnd,
this.onInteractionStart,
this.onInteractionUpdate,
this.panEnabled = true,
this.scaleEnabled = true,
this.transformationController,
@required this.child,
})
The only required parameter is child
which is a Widget
where the transformation will be applied on.
For this tutorial, we are going to use an image whose size is much larger than the size constraint.
static const String imageUrl = 'https://www.wallpapers13.com/wp-content/uploads/2016/01/Beautiful-lake-mountain-forest-desktop-wallpapers.jpg';
Here's a basic usage example. By wrapping the image inside an InteractiveViewer
widget, it enables the user to perform pan and scale on the image. The image is also resized to fit the size constraint by default. However, those behaviors can be customized which will be shown later in this tutorial.
InteractiveViewer(
child: Image.network(imageUrl),
)
Output:
Enable/Disable Scale
By default, scaling is enabled. To disable scaling, pass scaleEnabled
with the value set to false
.
InteractiveViewer(
scaleEnabled: false,
child: Image.network(imageUrl),
)
Output:
Setting Minimum and Maximum Scale
By default, the minimum and maximum scales are 0.8 and 2.5 respectively. The minimum scale value can be set by passing minScale
named argument with a double value, while the named argument for the maximum scale is maxScale
. Both minScale
and maxScale
cannot be null and must be greater than 0, with the value of minScale
must be less than maxScale
.
InteractiveViewer(
minScale: 0.5
maxScale: 10,
child: Image.network(imageUrl),
)
Enable/Disable Pan
By default, the user can perform pan gesture to view the other area of the child in case the current size of the child is bigger than the size constraint. To disable pan gesture, pass panEnabled
with the value set to false
.
InteractiveViewer(
panEnabled: false,
maxScale: 10,
child: Image.network(imageUrl),
)
Output:
Control Pan Direction
The default behavior allows panning in horizontal, vertical, and diagonal directions. To disable diagonal panning, set the alignPanAxis
to true
.
InteractiveViewer(
alignPanAxis: true,
child: Image.network(imageUrl),
)
Output:
Handle Interactions
There are some callback functions you can pass to get the detail of the interactions performed by the user. onInteractionStart
which accepts a function with a parameter of type ScaleStartDetails
can be used to get details of the touch when the user starts a scale or pan gesture. To get the details when the user ends the gesture, you can pass another callback function whose parameter type is ScaleEndDetails
as onInteractionEnd
named argument. Everytime the user moves the pointer on the screen, you can get the details by passing a callback function whose parameter type is ScaleUpdateDetails
as onInteractionUpdate
named argument.
InteractiveViewer(
child: Image.network(imageUrl),
onInteractionStart: (ScaleStartDetails scaleStartDetails) {
print('Interaction Start - Focal point: ${scaleStartDetails.focalPoint}'
', Local focal point: ${scaleStartDetails.localFocalPoint}'
);
},
onInteractionEnd: (ScaleEndDetails scaleEndDetails) {
print('Interaction End - Velocity: ${scaleEndDetails.velocity}');
},
onInteractionUpdate: (ScaleUpdateDetails scaleUpdateDetails) {
print('Interaction Update - Focal point: ${scaleUpdateDetails.focalPoint}'
', Local focal point: ${scaleUpdateDetails.localFocalPoint}'
', Scale: ${scaleUpdateDetails.scale}'
', Horizontal scale: ${scaleUpdateDetails.horizontalScale}'
', Vertical scale: ${scaleUpdateDetails.verticalScale}'
', Rotation: ${scaleUpdateDetails.rotation}'
);
},
)
To detect other type of gestures, you can use Flutter's GestureDetector
.
Setting TransformationController
You can control the transformation to be applied on the child by passing a custom TransformationController
instance as transformationController
named argument. By default, TransformationController
uses Matrix4.identity()
, which means no transformation is applied. You can create your own Matrix4
to customize the transformation.
The below example creates a Matrix4
which is used to build a TransformationController
.
static Matrix4 matrix4 = Matrix4(
2, 0, 0, 0,
0, 1, 0, 0,
0, 0, 4, 0,
0, 0, 0, 4
);
TransformationController controller = TransformationController(matrix4);
Then pass the created controller as transformationController
argument.
InteractiveViewer(
transformationController: controller,
child: Image.network(imageUrl),
)
Output:
InteractiveViewer
Parameters
Below is the list of named parameters supported by the constructor of InteractiveViewer
.
Key key
: The widget's key.bool alignPanAxis
: Whether panning is only allowed in the direction of the horizontal or the vertical axis. Defaults tofalse
.EdgeInsets boundaryMargin
: A margin for the visible boundaries of the child. Defaults toEdgeInsets.zero
.bool constrained
: Whether the size constraints are applied to the child.. Deaults totrue
.double maxScale
: The maximum allowed scale. Defaults to 2.5.double minScale
: The minimum allowed scale. Defaults to 0.8.GestureScaleEndCallback onInteractionEnd
: Called when the user ends a pan or scale gesture on the widget.GestureScaleEndCallback onInteractionStart
: Called when the user begins a pan or scale gesture on the widget.GestureScaleEndCallback onInteractionUpdate
: Called when the user updates a pan or scale gesture on the widget.bool panEnabled
: Whether pan gesture is allowed. Defaults totrue
.bool scaleEnabled
: Whether scale gesture is allowed. Defaults totrue
TransformationController transformationController
: ATransformationController
for the transformation performed on the child.Widget child
*: The widget where the transformation is performed.
*: required
ScaleStartDetails
Properties
Offset focalPoint
: The initial focal point of the pointers in contact with the screen, in global coordinates.Offset localFocalPoint
: The initial focal point of the pointers in contact with the screen, in local coordinates.
ScaleEndDetails
Properties
Velocity velocity
: The velocity of the last pointer to be lifted off of the screen.
ScaleUpdateDetails
Properties
Offset focalPoint
: The focal point of the pointers in contact with the screen, in global coordinates.Offset localFocalPoint
: The focal point of the pointers in contact with the screen, in local coordinates.double scale
: The scale implied by the average distance between the pointers in contact with the screen.double horizontalScale
: The scale implied by the average distance along the horizontal axis between the pointers in contact with the screen.double verticalScale
: The scale implied by the average distance along the vertical axis between the pointers in contact with the screen.double rotation
: The angle implied by the first two pointers to enter in contact with the screen.
Full Code
You can copy the below code if you want to try InteractiveViewer
.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Woolha.com Flutter Tutorial',
home: _InteractiveViewerExample(),
);
}
}
class _InteractiveViewerExample extends StatelessWidget {
static const String imageUrl = 'https://www.wallpapers13.com/wp-content/uploads/2016/01/Beautiful-lake-mountain-forest-desktop-wallpapers.jpg';
static Matrix4 matrix4 = Matrix4(
2, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
);
TransformationController controller = TransformationController(matrix4);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Woolha.com Flutter Tutorial'),
),
body: Center(
child: InteractiveViewer(
transformationController: controller,
// alignPanAxis: true,
// panEnabled: false,
// scaleEnabled: false,
// constrained: false,
// minScale: 0.5,
// maxScale: 10,
child: Image.network(imageUrl),
onInteractionStart: (ScaleStartDetails scaleStartDetails) {
print('Interaction Start - Focal point: ${scaleStartDetails.focalPoint}'
', Local focal point: ${scaleStartDetails.localFocalPoint}'
);
},
onInteractionEnd: (ScaleEndDetails scaleEndDetails) {
print('Interaction End - Velocity: ${scaleEndDetails.velocity}');
},
onInteractionUpdate: (ScaleUpdateDetails scaleUpdateDetails) {
print('Interaction Update - Focal point: ${scaleUpdateDetails.focalPoint}'
', Local focal point: ${scaleUpdateDetails.localFocalPoint}'
', Scale: ${scaleUpdateDetails.scale}'
', Horizontal scale: ${scaleUpdateDetails.horizontalScale}'
', Vertical scale: ${scaleUpdateDetails.verticalScale}'
', Rotation: ${scaleUpdateDetails.rotation}'
);
},
),
),
);
}
}
The InteractiveViewer
is a widget that makes it easy for Flutter developers to have a widget where the user can perform pan and zoom interactions. Although this tutorial only shows the examples for Image
, it can also be used for any type of Widget
.