flutter中photo_vew嵌套GestureDetector屏幕滑动冲突解决

flutter中photo_vew嵌套GestureDetector屏幕滑动冲突解决前言 业务场景为,在摄像机的播放画面上,按下手指左右上下滑动,摄像机跟随滑动。 由于播放页面使用了photo_view提供了画面图片的放大缩小和拖拽功能,导致和嵌套的要实现监听滑动的GestureDe

前言

业务场景为,在摄像机的播放画面上,按下手指左右上下滑动,摄像机跟随滑动。

由于播放页面使用了photo_view提供了画面图片的放大缩小和拖拽功能,导致和嵌套的要实现监听滑动的GestureDetector存在冲突的问题。

最终实现的解决方案为,在photo_vew默认状态下,让GestureDetector接收手指滑动事件,摄像机跟随旋转;在photo_vew放大状态下,GestureDetector不接收事件,让photo_vew处理放大图片的拖拽。比较好的解决了用户的使用场景问题

问题描述

onPanDown实现

首先,正常思路肯定是直接在Photoview的外部包裹GestureDetector,然后重写onPanDown,onPanUpdate方法,如下

body: GestureDetector(
        onPanDown: (e) {
          print('fuxiao:按下 $e');
        },
        onPanStart: (e) {
          print('fuxiao:开始 $e');
        },
        onPanCancel: () {
          print('fuxiao:取消');
        },
        onPanEnd: (e) {
          print('fuxiao:结束');
        },
        onPanUpdate: (e) {
          print('fuxiao:更新$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,
          ),
        ),
      ),

结果存在两个问题

1、onPanUpdate第一次滑动不触发,而触发onPanCancel方法,猜想是PhotoView处理事件导致的,上层树的事件取消,通过将上面注释代码打开验证,确实是PhotoView导致的问题

2、由于重写onPanDown方法,导致PhotoView,双指缩放画面功能失效

两个功能都不能正常工作,尝试解决这个问题,在网上浏览资料发现,GestureDetector并没有提供,双指的按下检测方法。

所以希望通过双指按下PhotoView处理事件,单指按下外层GestureDetector处理事件的方法行不通。

onHorizontalDragUpdate 实现

通过查看GestureDetector构造方法发现,提供了水平和竖直方向的检测方法

水平拖拽

  • onHorizontalDragStart 水平移动开始
  • onHorizontalDragUpdate 水平方向移动
  • onHorizontalDragEnd 水平移动结束

垂直拖拽

  • onVerticalDragStart 垂直移动开始
  • onVerticalDragUpdate 垂直移动
  • onVerticalDragEnd 垂直移动结束

通过重写上述方法,实际打印log发现,onHorizontalDragUpdate和onVerticalDragUpdate在单手移动屏幕时总会优先调用,代码如下

body: GestureDetector(
        behavior: HitTestBehavior.opaque,
        onHorizontalDragUpdate: (e) {
          print('fuxiao:水平$e');
        },
        onVerticalDragUpdate: (e) {
          print('fuxiao:垂直$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,
          ),
        ),
      ),

手指在屏幕上滑动,打印log

I/flutter ( 5191): fuxiao:水平DragUpdateDetails(Offset(1.1, 0.0))
I/flutter ( 5191): fuxiao:水平DragUpdateDetails(Offset(1.1, 0.0))
I/flutter ( 5191): fuxiao:水平DragUpdateDetails(Offset(1.8, 0.0))
I/flutter ( 5191): fuxiao:垂直DragUpdateDetails(Offset(0.0, -2.5))
I/flutter ( 5191): fuxiao:垂直DragUpdateDetails(Offset(0.0, -2.9))
I/flutter ( 5191): fuxiao:垂直DragUpdateDetails(Offset(0.0, -2.5))

并且,删除了onPanDown的重写方法,PhotoView双指放大缩小的功能也正常了,这样我们的需求基本上已经可以实现了,只不过还有一点小小的优化:PhotoView在画面放大状态下,左右滑动是移动画面,而不是回调onHorizontalDragUpdate方法

图片拖拽

跟相册中预览图片的效果是相似的。

而如果重写onHorizontalDragUpdate,会导致画面移动失效

水平移动

可以看到,放大以后,按下水平手指,水平滑动画面没有跟随移动,事件被上层消费了

解决的思路就是,当PhotoView放大或缩小状态时,禁止onHorizontalDragUpdate的调用

冲突解决

可以猜想,PhotoView应该提供了缩放状态的监听,查看PhotoView的构造方法

PhotoView({
    Key? key,
    required this.imageProvider,
    this.loadingBuilder,
    this.backgroundDecoration,
    this.gaplessPlayback = false,
    this.heroAttributes,
  	/// 缩放状态监听
    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);
/// 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;
/// A way to represent the step of the "doubletap gesture cycle" in which PhotoView is.
enum PhotoViewScaleState {
  initial,
  covering,
  originalSize,
  zoomedIn,
  zoomedOut,
}

这样就拿到了,PhotoView画面状态变化回调,只需要做一个处理,当state为initial时,才允许GestureDetector监听滑动事件,其他情况,走PhotoView的拖拽

结论

最终代码如下

这里直接贴上photo_view中example里的common_example_wrapper.dart代码

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: 滑动:$e');
    };
    if(widget.scaleChangedListener == null) {
      _scaleChangedListener = (PhotoViewScaleState statue) {
        print('fuxiao: 状态:$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,
          ),
        ),
      ),
    );
  }
}

最终效果如下

最终方案

总结

本文主要分析解决了,photo_view嵌套GestureDetector滑动监听的冲突问题,暂时先记录问题的解决,原理有时间再写一篇分析。

大家如果有遇到相同情况,并且有更好的解决方案,欢迎在评论区交流分享,感谢~

今天的文章flutter中photo_vew嵌套GestureDetector屏幕滑动冲突解决分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:http://bianchenghao.cn/18868.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注