[FIXED] How to fix SingleChildScrollView and InteractiveViewer gesture conflict

Issue

I use InteractiveViewer inside the SingleChildScrollView, this is my full code:

import 'package:flutter/material.dart';
import '../constants/images.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  final List<String> images = [Images.IMG1, Images.IMG2, Images.IMG3];

  MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: SingleChildScrollView(
          child: Column(
            children: [
              for (var image in images) ...[
                SizedBox(width:double.infinity,child: Divider(thickness: 5,)),
                Row(
                  children: [
                    Padding(
                      padding: const EdgeInsets.all(8.0),
                      child: ClipOval(
                        child: CircleAvatar(
                          radius: 24,
                          child: Image.asset(Images.PROFILE_IMAGE),
                        ),
                      ),
                    ),
                    Text('User1'),
                  ],
                ),
                InteractiveViewer(
                    child: Image.asset(
                  image,
                  fit: BoxFit.fitWidth,
                )),
                Row(
                  children: [
                    Padding(
                      padding: const EdgeInsets.all(8.0),
                      child: Icon(Icons.favorite),
                    ),
                    Padding(
                      padding: const EdgeInsets.all(8.0),
                      child: Icon(Icons.comment),
                    ),
                  ],
                )
              ]
            ],
          ),
        ),
      ),
    );
  }
}

The problem is when I try to zoom-in or zoom-out my InteractiveViewer, it usually not works in first try, and I need to try 5-6 times in different touch positions to work, it is very hard to use InteractiveViewer in SingleChildScrollView, but it works perfect without SingleChildScrollView, see the clip below:

enter image description here

Is there any solutions?

Solution

It seems that there is a fight between SingleChildScrollView and InteractiveViewer to take the touch action.

With the example of Instagram’s scrolling mechanism, if we place two fingers on the post and try to scroll, the scrolling action will not be done and somehow the scrolling will be temporarily disabled.
So if we can detect this two finger touch action:

thanks to:
https://stackoverflow.com/a/72421013/14083299

we can disable the scroll and then zoom in/out.

Here is a sample code:

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  MyApp({Key? key}) : super(key: key);

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final List<String> images = [
    'assets/prof2.jpg',
    'assets/prof2.jpg',
    'assets/prof2.jpg'
  ];

  final events = [];
  bool canScroll = true;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: SingleChildScrollView(
          physics: canScroll
              ? const ScrollPhysics()
              : const NeverScrollableScrollPhysics(),
          child: Listener(
            onPointerDown: (event) {
              events.add(event.pointer);
              print("new event");
            },
            onPointerUp: (event) {
              events.clear();
              print("events cleared");
              setState(() {
                canScroll = true;
              });
            },
            onPointerMove: (event) {
              if (events.length == 2) {
                setState(() {
                  canScroll = false;
                });
                //   int sensitivity = 8;
                //   if (event.delta.dy > sensitivity) {
                //     // code for two finger swipe up event

                //   } else if (event.delta.dy < -sensitivity) {
                //     // code for two finger swipe down event

                //   }
              }
            },
            child: Container(
              child: Column(
                children: [
                  for (var image in images) ...[
                    const SizedBox(
                        width: double.infinity,
                        child: Divider(
                          thickness: 5,
                        )),
                    Row(
                      children: [
                        Padding(
                          padding: const EdgeInsets.all(8.0),
                          child: ClipOval(
                            child: CircleAvatar(
                              radius: 24,
                              child: Image.asset('assets/prof2.jpg'),
                            ),
                          ),
                        ),
                        const Text('User1'),
                      ],
                    ),
                    InteractiveViewer(
                        child: Image.asset(
                      image,
                      fit: BoxFit.fitWidth,
                    )),
                    Row(
                      children: const [
                        Padding(
                          padding: EdgeInsets.all(8.0),
                          child: Icon(Icons.favorite),
                        ),
                        Padding(
                          padding: EdgeInsets.all(8.0),
                          child: Icon(Icons.comment),
                        ),
                      ],
                    )
                  ]
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
}

Answered By – samad karimi

Answer Checked By – Cary Denson (FixeMe Admin)

Leave a Reply

Your email address will not be published. Required fields are marked *