[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:
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)