Flutter - Using InteractiveViewer Widget Examples

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:

Flutter - InteractiveViewer - basic 

 

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:

Flutter - InteractiveViewer -   Disabled 

 

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:

Flutter - InteractiveViewer - Pan Disabled 

 

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:

Flutter - InteractiveViewer - Align Pan Axis 

 

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:

Flutter - InteractiveViewer - TransformationController 

 

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 to false.
  • EdgeInsets boundaryMargin: A margin for the visible boundaries of the child. Defaults to EdgeInsets.zero.
  • bool constrained: Whether the size constraints are applied to the child.. Deaults to true.
  • 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 to true.
  • bool scaleEnabled: Whether scale gesture is allowed. Defaults to true
  • TransformationController transformationController: A TransformationController 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.