likes
comments
collection
share

flutter-常用手势与阻止事件冒泡与响应

作者站长头像
站长
· 阅读数 3

前言

flutter 中,除了使用 TextButton 之外,使用最多的就是GestureDetector手势了,这篇文章就简单介绍下GestureDetector手势

下面是手势中常常配合使用的两个方法

//获取屏幕的size信息
MediaQuery.of(context).size
//获取当前组件size信息,组件默认持有,不必从build中传递
context.size

获取当前组件的偏移量,可以通过组件的context获取RenderBox,可以用来动态获取更多必要的计算信息

组件渲染完成之后,可以通过组价你的context参数,间接获取组件的偏移量
RenderBox box = context.findRenderObject() as RenderBox;
final local = box.localToGlobal(Offset.zero);
print(local);

GestureDetector

手势生效范围,默认为包裹子视图范围内(注意:子视图一般默认会填充,再加上居中效果容易误认为点击区间出问题),且默认点击事件默认阻止向下继续冒泡

GestureDetector(
    onTap: () {
        //默认的点击事件,和textbutton类似,一般使用这个,抬起时生效
    },
    onTapDown: (TapDownDetails details)  {
        //点击后立即触发,特殊场景使用,一般配合tap的其他来设置点击效果
        //TapDownDetails中有点击的位置等信息
    },
    onTapCancel: () {
        //用于点击事件的取消操作,一般用于配合上面你的方法定制点击事件
    },
    
    onLongPress: () {
        //长按触发事件,一般用于预览界面不适合添加额外按钮,增加长按手势触发躺床才做
    },
    
    onDoubleTap: () {
        //双击触发事件,由短暂的连续单机事件封装而成,一般用于web端操作,移动端少有
    },

    onVerticalDragUpdate: (DragUpdateDetails details) {
      //拖拽更新,pan实际走的也是这个,不信看看参数😂
      //世界坐标details.globalPosition
      //本地坐标details.localPosition
      onUpdate(details.localPosition.dy);
    },
    onVerticalDragDown: (DragDownDetails details) {
      //拖拽动作点击时触发一次,如果点击时需要更新位置等信息,可以调用
    },
    onVerticalDragStart: (DragStartDetails details) {
        //拖拽动作开始时触发,实际上和down方法选择一个即可
    },
    onVerticalDragEnd: (DragEndDetails details) {
      //拖拽结束,可以处理相关事件的结尾工作,或者恢复
    },
    
    //他们三个也是和拖拽逻辑一模一样,只不过换一个名字,参数类型都一样
    //应该是对不同端的开发习惯的支持
    onPanDown: (DragDownDetails details) {},
    onPanEnd: (DragEndDetails details) {},
    onVerticalDragStart: (DragStartDetails details) {},
    onPanUpdate: (DragUpdateDetails details) {},
    
    child: Container(
        color: Colors.black26,
        width: 20,
        child: const Text("点击吧"),
    ),
),

阻止事件冒泡与响应

一般情况下,按照其他端使用手势GestureDetectoronTaponTapDown等是默认阻止向上冒泡的,即:只会响应最上面一层视图的事件,然后事件就结束了,下面被挡住的就不会响应,如果也需要同时响应的话(可以直接调用其方法即可,这种比较少)

如果点击了某个视图,又想忽略内部的点击响应事件,以避免其影响到到父视图或祖父等视图的点击,可以使用 IgnorePointer 来包裹相应模块即可(且参数 ignoring 可以控制是否忽略生效,默认生效)

//阻止被包裹的点击事件响应,以便于父级或者更高级别上视图响应
const IgnorePointer(
    child: GestureDetector(child: const Text('点击我,底部被挡住的就会有反应了,但我的就没反应了')),
),

场景模拟:一个ListView.builderitem中有一个按钮,点击后有响应,这个场景不需要他响应,但其为三方组件,自己改不了,因此需要阻止事件的响应,那样里面的按钮使用 IgnorePointer 包裹即可

例如:

下面的就会出现手势冲突,可以使用 IgnorePointer包裹里面的 GestureDetector(实际上拖拽等手势,也可能会出现不同层级的响应冲突问题),其会忽略掉被包裹的手势,阻止其向上传递冒泡

//默认响应案例
//这样点击文字就会响应顶部事件了,即:这里打印顶部+索引 
ListView.builder(
  itemCount: 20,
  itemBuilder: (BuildContext context, int index) {
    return GestureDetector(
      onTapDown: (TapDownDetails details) {
        print("底部$index");
      },
      child: Container(
        height: 80,
        color: Colors.yellow,
        alignment: Alignment.center,
        child: GestureDetector(
          onTapDown: (TapDownDetails details) {
            print("顶部$index");
          },
          child: Text("哈哈哈$index"),
          ),
        )
    );
  },
);

//阻止顶部按钮响应的解决方案,IgnorePointer,且参数 ignoring 可以控制是否有效
//这样点击文字就会响应 底部事件了,即:这里打印底部+索引 
ListView.builder(
  itemCount: 20,
  itemBuilder: (BuildContext context, int index) {
    return GestureDetector(
      onTapDown: (TapDownDetails details) {
        print("底部$index");
      },
      child: Container(
        height: 80,
        color: Colors.yellow,
        alignment: Alignment.center,
        child: IgnorePointer(
          child: GestureDetector(
            onTapDown: (TapDownDetails details) {
              print("顶部$index");
            },
            child: Text("哈哈哈$index"),
          ),
        ),
      ),
    );
  },
);

最后

可以点赞收藏,以后用到的时候,可以打开观看一下😂