preface
In a service scenario, press your finger and slide the camera up and down on the playing screen.
Because the playing page uses photo_view to provide the functions of zooming in and out and dragging pictures, it conflicts with the nested GestureDetector to monitor sliding.
The final solution is to make the GestureDetector receive the finger sliding event under the default state of photo_VEW, and the camera follows the rotation; In the photo_VEW zoom state, the GestureDetector does not receive events and lets Photo_VEW handle drag-and-drop of the zoom image. It better solves the problem of user’s usage scenario
Problem description
OnPanDown implementation
First of all, the normal idea is to wrap the GestureDetector directly outside the Photoview, and then rewrite the onPanDown and onPanUpdate methods as follows
body: GestureDetector(
onPanDown: (e) {
print('fuxiao: press$e');
},
onPanStart: (e) {
print('fuxiao: start$e');
},
onPanCancel: () {
print('fuxiao: cancel');
},
onPanEnd: (e) {
print('end of fuxiao:');
},
onPanUpdate: (e) {
print('fuxiao: update$e');
},
child: Container(
constraints: BoxConstraints.expand(
height: MediaQuery.of(context).size.height,
),
// child: Container(
// color: Colors.blue,
// width: 400,
// height: 400,
// )
child: PhotoView(
imageProvider: imageProvider,
loadingBuilder: loadingBuilder,
backgroundDecoration: backgroundDecoration,
minScale: minScale,
maxScale: maxScale,
initialScale: initialScale,
basePosition: basePosition,
filterQuality: filterQuality,
disableGestures: disableGestures,
errorBuilder: errorBuilder,
),
),
),
Copy the code
There are two problems with the result
1. OnPanUpdate does not trigger when it slides for the first time, but the onPanCancel method is triggered. It is assumed that the event is caused by PhotoView processing, and the event of the upper tree is cancelled
2. Due to the rewriting of onPanDown method, the functions of PhotoView and double finger zooming in and out of the picture failed
Neither of the two functions can work properly. Trying to solve this problem, I found that the GestureDetector does not provide a two-finger press detection method after browsing online.
So while you want to process the event by pressing the PhotoView with two fingers, you can’t process the event by pressing the outer GestureDetector with one finger.
OnHorizontalDragUpdate implementation
The GestureDetector constructor provides both horizontal and vertical detection methods
Horizontal dragging
- OnHorizontalDragStart Horizontal movement begins
- OnHorizontalDragUpdate moves horizontally
- OnHorizontalDragEnd moves horizontally to end
Vertical drag
- OnVerticalDragStart vertical movement starts
- OnVerticalDragUpdate moves vertically
- OnVerticalDragEnd moves vertically to end
By overriding the above method and actually printing the log, onHorizontalDragUpdate and onVerticalDragUpdate are always called first when moving the screen with one hand, as shown below
body: GestureDetector(
behavior: HitTestBehavior.opaque,
onHorizontalDragUpdate: (e) {
print('fuxiao: level$e');
},
onVerticalDragUpdate: (e) {
print('fuxiao: vertical$e');
},
child: Container(
constraints: BoxConstraints.expand(
height: MediaQuery.of(context).size.height,
),
child: PhotoView(
imageProvider: imageProvider,
loadingBuilder: loadingBuilder,
backgroundDecoration: backgroundDecoration,
minScale: minScale,
maxScale: maxScale,
initialScale: initialScale,
basePosition: basePosition,
filterQuality: filterQuality,
disableGestures: disableGestures,
errorBuilder: errorBuilder,
),
),
),
Copy the code
Swipe your finger across the screen to print a log
I/flutter (5191): fuXIAO: level DragUpdateDetails(Offset(1.1, 0.0)) Fuxiao: horizontal DragUpdateDetails(Offset(1.1, 0.0)) I/ FLUTTER (5191): Fuxiao: horizontal DragUpdateDetails(Offset(1.8, 0.0)) I/ FLUTTER (5191): Fuxiao: Vertical DragUpdateDetails(Offset(0.0, -2.5)) I/ FLUTTER (5191): Fuxiao: Vertical DragUpdateDetails(Offset(0.0, -2.9)) I/ FLUTTER (5191): fuxiao: vertical DragUpdateDetails(Offset(0.0, -2.5))Copy the code
In addition, the onPanDown override method was removed, and the PhotoView double finger zoom in and out function was also normal, so we can basically achieve our requirements, but there is a little optimization: When PhotoView is zoomed in, swiping left and right moves the image instead of calling the onHorizontalDragUpdate method
The effect is similar to that of a preview picture in an album.
Overwriting onHorizontalDragUpdate will invalidate screen movement
As you can see, after zooming in, press the horizontal finger, the horizontal slide screen does not follow the movement, and the event is consumed by the upper layer
The idea is to disable onHorizontalDragUpdate when PhotoView zooms in or out of state
Conflict resolution
As you can guess, PhotoView should provide a listener for the zoom state to see the constructor of the PhotoView
PhotoView({
Key? key,
required this.imageProvider,
this.loadingBuilder,
this.backgroundDecoration,
this.gaplessPlayback = false.this.heroAttributes,
/// Zoom state monitor
this.scaleStateChangedCallback,
this.enableRotation = false.this.controller,
this.scaleStateController,
this.maxScale,
this.minScale,
this.initialScale,
this.basePosition,
this.scaleStateCycle,
this.onTapUp,
this.onTapDown,
this.onScaleEnd,
this.customSize,
this.gestureDetectorBehavior,
this.tightMode,
this.filterQuality,
this.disableGestures,
this.errorBuilder,
this.enablePanAlways,
}) : child = null,
childSize = null.super(key: key);
Copy the code
/// A [Function] to be called whenever the scaleState changes, this happens when the user double taps the content ou start to pinch-in.
final ValueChanged<PhotoViewScaleState>? scaleStateChangedCallback;
Copy the code
/// A way to represent the step of the "doubletap gesture cycle" in which PhotoView is.
enum PhotoViewScaleState {
initial,
covering,
originalSize,
zoomedIn,
zoomedOut,
}
Copy the code
When state is initial, the GestureDetector is allowed to listen for the slide event. Otherwise, the PhotoView drags
conclusion
The final code is as follows
The common_example_wrapper.dart code from example in photo_view is directly pasted here
import 'package:flutter/material.dart';
import 'package:photo_view/photo_view.dart';
class CommonExampleRouteWrapper extends StatefulWidget {
const CommonExampleRouteWrapper({
this.imageProvider,
this.loadingBuilder,
this.backgroundDecoration,
this.minScale,
this.maxScale,
this.initialScale,
this.basePosition = Alignment.center,
this.filterQuality = FilterQuality.none,
this.disableGestures,
this.errorBuilder,
this.scaleChangedListener
});
final ImageProvider? imageProvider;
final LoadingBuilder? loadingBuilder;
final BoxDecoration? backgroundDecoration;
final dynamic minScale;
final dynamic maxScale;
final dynamic initialScale;
final Alignment basePosition;
final FilterQuality filterQuality;
final bool? disableGestures;
final ImageErrorWidgetBuilder? errorBuilder;
final ValueChanged<PhotoViewScaleState>? scaleChangedListener;
@override
_CommonExampleRouteWrapperState createState() => _CommonExampleRouteWrapperState();
}
class _CommonExampleRouteWrapperState extends State<CommonExampleRouteWrapper> {
ValueChanged<PhotoViewScaleState>? _scaleChangedListener;
bool canZoomControl = true;
GestureDragUpdateCallback? updateCallback;
@override
void initState() {
updateCallback = (e) {
print('fuxiao: slide:$e');
};
if(widget.scaleChangedListener == null) {
_scaleChangedListener = (PhotoViewScaleState statue) {
print('fuxiao: status:$statue');
switch(statue) {
case PhotoViewScaleState.initial:
canZoomControl = true;
break;
default:
canZoomControl = false;
break;
}
setState(() {
});
};
} else {
_scaleChangedListener = widget.scaleChangedListener;
}
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: GestureDetector(
onHorizontalDragUpdate: canZoomControl ? updateCallback : null,
onVerticalDragUpdate: canZoomControl ? updateCallback : null, child: Container( constraints: BoxConstraints.expand( height: MediaQuery.of(context).size.height, ), child: PhotoView( imageProvider: widget.imageProvider, loadingBuilder: widget.loadingBuilder, backgroundDecoration: widget.backgroundDecoration, scaleStateChangedCallback: _scaleChangedListener, minScale: widget.minScale, maxScale: widget.maxScale, initialScale: widget.initialScale, basePosition: widget.basePosition, filterQuality: widget.filterQuality, disableGestures: widget.disableGestures, errorBuilder: widget.errorBuilder, ), ), ), ); }}Copy the code
The end result is as follows
conclusion
This paper mainly analyzes and solves the conflict problem of sliding listening of photo_view nested GestureDetector. For the time being, the solution of the problem will be recorded first, and another analysis of the principle will be written later.
If you have encountered the same situation, and have a better solution, welcome to share in the comments section, thanks ~