Ammar Mohamed
Ammar Mohamed

Reputation: 790

How to zoom in SVG picture freely in Flutter

I am using flutter_svg package to show svg picture in flutter but I want to be able to zoom into this picture without losing the svg properties of keeping the resolution, I have tried to use photo_view for zooming capabilities and flutter_svg_provider to use svg picture as image provider but it's not working properly.

So is there a way to be able to zoom and move freely in SVG picture in Flutter?

Upvotes: 4

Views: 1093

Answers (2)

mandy8055
mandy8055

Reputation: 6735

To zoom and move freely in an SVG picture you can simply use InteractiveViewer widget which is built in Flutter SDK itself and allows you to zoom and pan its child Widget. The minScale and maxScale properties control the minimum and maximum zoom levels. Also, do not forget to add the image asset to pubspec.yaml1 in case you're using local svg. If you're using network image, then you can use the SVG SvgPicture.network function to load an SVG image from a URL. Also, you can use SvgPicture.asset(like in the below example) or SvgPicture.file if your SVG is located in your app's assets or device storage, respectively.

// ...rest
body: Center(
  child: InteractiveViewer(
    minScale: 0.5,
    maxScale: 4.0,
    child: SvgPicture.asset(
      "assets/images/sample.svg", // Replace with the name of your SVG file
       height: 200,
     ),
   ),
),

SAMPLE DEMO

Output:

Output for sample run


1Adding an image asset:

  1. Create an assets folder at the root level of your project.

  2. Inside the assets folder, you can create subdirectories to organize your assets, such as an images folder for image assets.

  3. Place the svg image inside the images folder.

  4. Open your pubspec.yaml file and add the assets section under the flutter key. Something like:

    flutter:
      uses-material-design: true
      assets:
        - assets/images/your_image.svg
    

Upvotes: 4

MendelG
MendelG

Reputation: 20098

Result

Explanation

You can use InteractiveViewer to achieve what you want with custom animations.

Code

class PinchZoomSvg extends StatefulWidget {
  final String urlImage;

  const PinchZoomSvg({
    Key? key,
    required this.urlImage,
  }) : super(key: key);

  @override
  State<PinchZoomSvg> createState() => _PinchZoomSvgState();
}

class _PinchZoomSvgState extends State<PinchZoomSvg>
    with SingleTickerProviderStateMixin {
  late TransformationController controller;
  late AnimationController animationController;
  Animation<Matrix4>? animation;

  final double minScale = 1;
  final double maxScale = double.infinity;

  @override
  void initState() {
    super.initState();

    controller = TransformationController();
    animationController = AnimationController(
      vsync: this,
      duration: Duration(milliseconds: 200),
    )
      ..addListener(() => controller.value = animation!.value)
      ..addStatusListener((status) {});
  }

  @override
  void dispose() {
    controller.dispose();
    animationController.dispose();

    super.dispose();
  }

  @override
  Widget build(BuildContext context) => Center(
        child: buildImage(),
      );

  Widget buildImage() => Builder(
        builder: (context) => InteractiveViewer(
          transformationController: controller,
          clipBehavior: Clip.none,
          panEnabled: false,
          minScale: minScale,
          maxScale: maxScale,
          onInteractionStart: (details) {
            if (details.pointerCount < 2) return;

            animationController.stop();
          },
          onInteractionEnd: (details) {
            // only executed if interaction ends with no fingers on screen
            if (details.pointerCount != 1) return;

            resetAnimation();
          },
          child: AspectRatio(
            aspectRatio: 1,
            child: ClipRRect(
                borderRadius: BorderRadius.circular(20),
                child: SvgPicture.asset(
                  widget.urlImage,
                  fit: BoxFit.cover,
                )),
          ),
        ),
      );

  void resetAnimation() {
    animation = Matrix4Tween(
      begin: controller.value,
      end: Matrix4.identity(),
    ).animate(
      CurvedAnimation(parent: animationController, curve: Curves.easeInOut),
    );

    animationController.forward(from: 0);
  }
}

You can now use the widget whenever you want:

Center(
  child: PinchZoomSvg(
    urlImage: 'assets/logo.svg',
  ),
),

Credits

The code in this post was inspired by this YouTube video - please watch it (until minute 3:24) as it will explain the code in detail.

Notes

  • Don't forget to import flutter_svg.
  • Add the SVG file to your pubspec.yaml.

Upvotes: 2

Related Questions