What is a zoomable widget in Flutter?
A zoomable widget in Flutter is a widget that allows users to zoom in and out of an image or other content. In Flutter, you can use the InteractiveViewer
widget to create a zoomable interface. This widget allows users to zoom and pan the content within it. It is a low-level widget that provides basic zooming and panning capabilities.
There are also several third-party libraries available that provide more advanced zooming and panning features, such as the ability to zoom from the centre of the content or to restrict the zoom level to a certain range. Some popular third-party libraries for creating zoomable widgets in Flutter include photo_view
and zoom_pinch_overlay
.
With these widgets, you can create engaging user experiences that allow users to explore images or other content in detail. Whether you choose to use the built-in InteractiveViewer
widget, or a third-party library, creating a zoomable widget in Flutter is a great way to add interactivity to your app.
What is the Flutter InteractiveViewer widget?
The InteractiveViewer
is a Flutter widget that provides an interactive experience to the user by allowing them to zoom and pans the child widget. It’s particularly useful when you have a large or complex widget that you want the user to be able to interact with and explore in detail.
The InteractiveViewer
widget works by detecting gestures made on the screen, such as pinch-to-zoom or panning gestures, and adjusting the child widget accordingly. This creates a smooth and intuitive experience for the user, who can zoom in or out of the widget, and pan to different areas.
One advantage of using the InteractiveViewer
is that it’s simple to use. All you need to do is wrap the widget you want to make interactive in an InteractiveViewer
widget, and you’re good to go. This makes it a great choice for developers who need to add interactive elements to their Flutter applications quickly and easily.
InteractiveViewer(,
child: Image.asset("assets/logo.png"),
)
How to use the Zoomable InteractiveViewer in Flutter
Here is an example of how to use the Flutter InteractiveViewer:
@override
Widget build(BuildContext context) {
double screenWidth = MediaQuery.of(context).size.width;
return Scaffold(
appBar: AppBar(
title: Text('flutterassets.com'),
),
body: Center(
child: Container(
width: screenWidth,
height: screenWidth,
margin: EdgeInsets.all(20),
decoration: BoxDecoration(
border: Border.all(width: 1, color: Colors.grey)
),
child: InteractiveViewer(
maxScale: 3.0,
minScale: 0.5,
child: Image.asset("assets/logo.png"),
),
),
)
);
}

This code first uses a Center
widget to centre the content within the screen. Then, a Container
widget is used to give a defined width and height to the content. In this case, the width and height are set to be the same as the width of the screen. The Container
widget also has a margin of 20 and a border with a width of 1 and grey colour for visual purposes.
The InteractiveViewer
widget is then used as a child of the Container
widget. The maxScale
property is set to 3.0
and the minScale
property is set to 0.5
to restrict the zooming factor. The child of the InteractiveViewer
is an image asset with the path “assets/logo.png”.
With this code, the user can zoom in and out on the image and move it around to get a better view. The InteractiveViewer
also handles user interactions such as panning and scaling. The minimum and maximum zoom levels are restricted by the minScale
and maxScale
properties.
Note: At the time of writing I have an issue with minScale
. Basically, it doesn’t work for me.
Reset InteractiveViewer widget after the interaction
final zoomTransformationController = TransformationController();
void _resetZoom(){
zoomTransformationController.value = Matrix4.identity();
print('reset zoom');
}
@override
Widget build(BuildContext context) {
double screenWidth = MediaQuery.of(context).size.width;
return Scaffold(
appBar: AppBar(
title: Text('flutterassets.com'),
),
body:
Center(
child: Container(
width: screenWidth,
height: screenWidth,
margin: EdgeInsets.all(20),
decoration: BoxDecoration(
border: Border.all(width: 1, color: Colors.grey)
),
child: InteractiveViewer(
boundaryMargin: EdgeInsets.zero,
transformationController: zoomTransformationController,
maxScale: 3.0,
minScale: 1,
onInteractionEnd: (ScaleEndDetails details){
setState(() {
_resetZoom();
});
},
child: Image.network('https://picsum.photos/500?image=9'),
),
),
),
);
}
The main feature of this code is the zoom reset function, which is called when the user ends their interaction with the InteractiveViewer
. The function used zoomTransformationController.value = Matrix4.identity();
, which reset the InteractiveViewer
widget to the default zoom level.
The InteractiveViewer
widget uses the zoomTransformationController
as its transformationController and sets its maxScale
and minScale
properties to 3.0 and 1 respectively. The onInteractionEnd property is set to the _resetZoom
function, so that the zoom will be reset every time the user stops interacting with the image.
You can also reset the InteractiveViewer
with this code:
void _resetZoom(){
final zoomFactor = 1.0;
final xTranslate = 0.0;
final yTranslate = 0.0;
zoomTransformationController.value.setEntry(0, 0, zoomFactor);
zoomTransformationController.value.setEntry(1, 1, zoomFactor);
zoomTransformationController.value.setEntry(2, 2, zoomFactor);
zoomTransformationController.value.setEntry(0, 3, -xTranslate);
zoomTransformationController.value.setEntry(1, 3, -yTranslate);
print('reset zoom');
}
Disclaimer: I should be ashamed here but I do not understand how the above reset works, sorry. The solution was copied from this link.
Flutter InteractiveViewer with Buttons Zoom In and Zoom Out
The code of the InteractiveViewer
widget with a zoom-in, zoom-out, and reset button. The InteractiveViewer
is used to zoom in and out of an image:
final zoomTransformationController = TransformationController();
void _resetZoom(){
zoomTransformationController.value = Matrix4.identity();
print('reset zoom');
}
void _zoomIn(){
zoomTransformationController.value.scale(1.1);
}
void _zoomOut(){
zoomTransformationController.value.scale(0.9);
}
@override
Widget build(BuildContext context) {
double screenWidth = MediaQuery.of(context).size.width;
return Scaffold(
appBar: AppBar(
title: Text('flutterassets.com'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(height: 20,),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
ElevatedButton(
onPressed: (){
setState(() {
_zoomIn();
});
},
child: Icon(Icons.zoom_in, size: 60,)
),ElevatedButton(
onPressed: (){
setState(() {
_resetZoom();
});
},
child: Icon(Icons.restart_alt, size: 60,)
),ElevatedButton(
onPressed: (){
setState(() {
_zoomOut();
});
},
child: Icon(Icons.zoom_out, size: 60,)
),
],
),
Container(
width: screenWidth,
height: screenWidth,
margin: EdgeInsets.all(20),
decoration: BoxDecoration(
border: Border.all(width: 1, color: Colors.grey)
),
child: InteractiveViewer(
boundaryMargin: EdgeInsets.zero,
transformationController: zoomTransformationController,
maxScale: 3.0,
minScale: 1,
child: Image.network('https://picsum.photos/500?image=9'),
),
),
],
),
);
}

The code defines three functions: _resetZoom()
, _zoomIn()
, and _zoomOut()
. The _resetZoom()
function sets the matrix of the zoomTransformationController
back to its original state. The _zoomIn()
function increases the zoom factor by scaling the matrix of the zoomTransformationController
by 1.1. The _zoomOut()
function decreases the zoom factor by scaling the matrix of the zoomTransformationController
by 0.9.
In the build
method, the code creates a Scaffold
widget with an app bar and a body that contains a column of widgets. The first row of widgets in the body contains three elevated buttons with zoom-in, reset, and zoom-out icons. These buttons are used to trigger the _zoomIn()
, _resetZoom()
, and _zoomOut()
functions respectively.
The second widget in the body is a container that contains the InteractiveViewer widget. The InteractiveViewer
widget uses the zoomTransformationController
as its transformationController
and the image is loaded from a network source. The InteractiveViewer
has properties maxScale
and minScale
set to 3.0 and 1.0, which limits the zoom factor to a maximum of 3.0 and a minimum of 1.0.
With this code, users can zoom in and out of the image using the zoom-in and zoom-out buttons and reset the zoom to its original state using the reset button.
Flutter InteractiveViewer with Double Tap zoom in an out
Here is a code showing how you can use double tap with InteractiveViewe to zoom in and zoom out:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final zoomTransformationController = TransformationController();
bool _zoomedIn = false;
void _DoubleTapZoomIn(){
zoomTransformationController.value = Matrix4.identity()..scale(3.0);
}
void _DoubleTapZoomOut(){
zoomTransformationController.value = Matrix4.identity();
}
@override
Widget build(BuildContext context) {
double screenWidth = MediaQuery.of(context).size.width;
return Scaffold(
appBar: AppBar(
title: Text('flutterassets.com'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
GestureDetector(
onDoubleTap: (){
if(_zoomedIn == false){
_DoubleTapZoomIn();
}else {
_DoubleTapZoomOut();
}
setState(() {
_zoomedIn = !_zoomedIn;
});
},
child: Container(
width: screenWidth,
height: screenWidth,
margin: EdgeInsets.all(20),
decoration: BoxDecoration(
border: Border.all(width: 1, color: Colors.grey)
),
child: InteractiveViewer(
boundaryMargin: EdgeInsets.zero,
transformationController: zoomTransformationController,
maxScale: 3.0,
minScale: 1,
child: Image.network('https://picsum.photos/500?image=9'),
),
),
),
],
),
);
}
}
Here’s how it works:
The final zoomTransformationController = TransformationController();
line creates an instance of the TransformationController
class, which will be used to control the zoom of the image. The _zoomedIn
boolean variable is used to keep track of whether the image is currently zoomed in or not.
The _DoubleTapZoomIn()
and _DoubleTapZoomOut()
methods are called when the user double-taps the image to either zoom in or zoom out. The zoomTransformationController.value = Matrix4.identity()..scale(3.0);
line sets the zoom level to 3.0, which means the image will be zoomed in by a factor of 3. And zoomTransformationController.value = Matrix4.identity();
line sets the zoom level back to 1.0, which is the default value and means that the image is not zoomed in or out.
In the build
method, the code creates a Scaffold widget that contains a single InteractiveViewer
widget. The InteractiveViewer
widget takes the image to be displayed as its child
and allows it to be zoomed. The transformationController
property is set to the zoomTransformationController
created earlier so that the InteractiveViewer
will use this instance to control the zoom level of the image.
The GestureDetector
widget is used to detect when the user double-taps the image. When a double-tap is detected, the code checks the value of _zoomedIn
to see whether the image is currently zoomed in or not. If the image is not zoomed in, the _DoubleTapZoomIn()
method is called to zoom in the image. If the image is already zoomed in, the _DoubleTapZoomOut()
method is called to zoom it back out. The setState
method is then called to update the _zoomedIn
variable to reflect the new zoom state.
How to use the photo_view widget in Flutter
photo_view
is a Flutter library for creating zoomable images. It is a versatile and easy-to-use widget that provides many advanced zooming and panning features, including the ability to zoom from the centre of the image and to restrict the zoom level to a certain range.
To use photo_view
in your Flutter project, you need to install it first. You can do this by adding the following line to your pubspec.yaml
file:
dependencies:
photo_view: ^0.14.0
Next, you need to import the library into your Dart code.
import 'package:photo_view/photo_view.dart';
Here’s an example of how to use photo_view
to display a network image:
@override
Widget build(BuildContext context) {
double screenWidth = MediaQuery.of(context).size.width;
double screenHeight = MediaQuery.of(context).size.height;
return Scaffold(
appBar: AppBar(
title: Text('flutterassets.com'),
),
body: Container(
width: screenWidth,
height: screenHeight,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 300,
height: 300,
child: PhotoView(
minScale: 0.5,
maxScale: 2.0,
imageProvider: NetworkImage('https://picsum.photos/500?image=9'),
),
),
],
)
)
);
}
In this example, we use the PhotoView
widget to display an image from a URL. We also have two properties: minScale
and maxScale
. minScale
sets the minimum amount that the image can be scaled, in this case, it’s set to 0.5. maxScale
sets the maximum amount that the image can be scaled, in this case, it’s set to 2.0
.
Flutter photo_view with Buttons Zoom In and Zoom Out
Here’s an updated version of the previous code example that includes three buttons to zoom in, reset, and zoom out:
double _scale = 0.5;
void _resetScale() {
setState(() {
_scale = 0.5;
});
}
void _incrementScale() {
setState(() {
_scale += 0.1;
});
}
void _decrementScale() {
setState(() {
_scale -= 0.1;
});
}
@override
Widget build(BuildContext context) {
double screenWidth = MediaQuery.of(context).size.width;
double screenHeight = MediaQuery.of(context).size.height;
return Scaffold(
appBar: AppBar(
title: Text('flutterassets.com'),
),
body: Container(
width: screenWidth,
height: screenHeight,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 300,
height: 300,
child: PhotoView(
initialScale: _scale,
minScale: 0.5,
maxScale: 2.0,
imageProvider: NetworkImage('https://picsum.photos/500?image=9'),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
child: Text('Zoom In'),
onPressed: _incrementScale,
),
SizedBox(width: 10),
ElevatedButton(
child: Text('Reset'),
onPressed: _resetScale,
),
SizedBox(width: 10),
ElevatedButton(
child: Text('Zoom Out'),
onPressed: _decrementScale,
),
],
),
],
)
)
);
}
In this example, we’ve added a _scale
variable to keep track of the current scale of the image. We’ve also added three functions to reset the scale, zoom in, and zoom out. These functions update the _scale
variable and trigger a rebuild of the widget using setState
.
We’ve also added a row of buttons at the bottom of the screen, each with its own callback to the corresponding zoom function. The initialScale
property of the PhotoView
widget is set to _scale
, so that it updates dynamically as the buttons are pressed.
How to use the zoom_pinch_overlay widget in Flutter
zoom_pinch_overlay
is a third-party package for Flutter that provides a zoomable and pinchable image overlay widget. It allows you to add pinch and zoom capabilities to an image in your Flutter app. To use zoom_pinch_overlay
, you’ll need to install the package from pub.dev by adding the following dependency to your pubspec.yaml
file:
dependencies:
zoom_pinch_overlay: ^1.3.2
Once the package is installed, you can use it in your Flutter app by importing the package and adding the ZoomOverlay
widget to your widget tree.
Here’s an example of how you could use the ZoomOverlay
widget:
import 'package:zoom_pinch_overlay/zoom_pinch_overlay.dart';
Center(
child: Container(
width: screenWidth,
height: screenHeight,
child: ZoomOverlay(
minScale: 0.5,
maxScale: 2.0,
twoTouchOnly: true,
child: Image.network('https://picsum.photos/500?image=9'),
),
),
)
In the example above, we wrap the Image.network
widget with the ZoomOverlay
widget. This allows us to add pinch and zoom capabilities to the image. The ZoomOverlay
widget takes the child
widget as its only required property, so you simply need to pass in the image widget that you want to make zoomable.
Flutter zoom_pinch_overlay error (Null check operator used on a null value)
At the time of writing, I have an error with this widget. Basically, the twoTouchOnly
parameter must be set to true
in order to remove the error.
oomOverlay(
minScale: 0.5,
maxScale: 2.0,
twoTouchOnly: true,
child: Image.network('https://picsum.photos/500?image=9'),
),