In my Flutter application, I'm using both PageView
and InteractiveViewer
to display and interact with images. I've encountered an issue where, after zooming into an image using InteractiveViewer
, if I try to drag the image, the page switches immediately. This behavior seems to be due to a gesture conflict between PageView
and InteractiveViewer
.
What I've tried:
- I've looked into the gestures of both widgets to see if there's a way to prioritize one over the other.
- I've checked the following StackOverflow post which suggests executing paging only when the image is not zoomed in: link. However, this is not the behavior I want.
Expected Behavior: Even when the image is zoomed in, I want the application to switch to the next page only if I drag to the very edge of the zoomed image.
Actual Behavior: Currently, after zooming in, any drag gesture immediately switches the page.
Any suggestions or solutions to handle this gesture conflict would be greatly appreciated.
What I was expecting:
- I wanted a solution where, even when the image is zoomed in using
InteractiveViewer
, the application would switch to the next page only if I drag to the very edge of the zoomed image. - Instead of the current behavior where, after zooming in, any drag gesture immediately switches the page.
mycode:
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:preload_page_view/preload_page_view.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'PhotoView + Interactiveviewer',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const GalleryExample(),
);
}
}
class GalleryExample extends StatefulWidget {
const GalleryExample({super.key});
@override
_GalleryExampleState createState() => _GalleryExampleState();
}
class _GalleryExampleState extends State<GalleryExample> {
late int selectedIndex;
final TransformationController _transformationController = TransformationController();
late TapDownDetails _doubleTapDetails;
List<String> pageItems = [
"https://user0514.cdnw.net/shared/img/thumb/12redsugar721_TP_V.jpg",
"https://user0514.cdnw.net/shared/img/thumb/05redsugar721_TP_V.jpg"
];
void _handleDoubleTap() {
print("double tap");
final currentScale = _transformationController.value.getMaxScaleOnAxis();
if (currentScale > 1.0) {
_transformationController.value = Matrix4.identity();
} else {
final position = _doubleTapDetails.localPosition;
_transformationController.value = Matrix4.identity()
..translate(-position.dx, -position.dy)
..scale(2.0);
}
}
@override
void initState() {
super.initState();
selectedIndex = 0;
}
@override
Widget build(
BuildContext context,
) {
return Scaffold(
backgroundColor: Colors.black,
body: GestureDetector(
behavior: HitTestBehavior.opaque,
child: SafeArea(
child: Stack(
children: [
Container(
color: Colors.black,
child: PageView.builder(
pageSnapping: true,
reverse: true,
itemCount: pageItems.length,
itemBuilder: (context, index) {
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return InteractiveViewer(
maxScale: 5.0,
minScale: 1.0,
transformationController: _transformationController,
onInteractionEnd: (details) {
if (_transformationController.value.getMaxScaleOnAxis() == 1.0) {
_transformationController.value = Matrix4.identity();
}
},
child: GestureDetector(
onDoubleTapDown: (details) {
_doubleTapDetails = details;
},
onDoubleTap: _handleDoubleTap,
child: Image.network(
pageItems[index],
fit: BoxFit.contain,
),
),
);
},
);
},
),
)
],
),
),
));
}
}