flutter interactiveviewer zoom buttons

How to use Flutter Zoomable Widget with examples

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"),
),
),
)
);
}
flutter interactiveviewer zoom

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'),
            ),
          ),
        ],
      ),
  );
}
flutter interactiveviewer zoom buttons

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'),
),