Flutter踩坑系列:滑动手势监听不到

作者:Dreamy

问题描述

在PageView外部使用GestureDetector监听不到左滑手势。
如下代码,水平滑动的PageView只监听到了垂直方向的拖拽手势,却监听不到水平方向的拖拽手势。

GestureDetector(
  onVerticalDragStart: (details) => print('onVerticalDragStart'),
  onVerticalDragEnd: (details) => print('onVerticalDragEnd'),
  onHorizontalDragStart: (details) => print('onHorizontalDragStart'),
  onHorizontalDragEnd: (details) => print('onHorizontalDragEnd'),
  child: PageView(
    children: [
      Container(color: Colors.amber),
      Container(color: Colors.blue),
      Container(color: Colors.deepOrangeAccent),
    ],
  ),
);
日志

原因分析

水平手势已经被横向滑动的PageView监听,所以通过GestureDetector是无法再次监听的,只能使用Listener对原始指针进行监听,进而达到监听横向滑动的效果。下文具体介绍Flutter的手势监听机制。

Pointer

Pointer Event指针事件是指最原始的触摸事件,一次事件包括触摸到屏幕、在屏幕上移动到离开屏幕。 使用Listener组件来监听指针事件,基本的回调有onPointerDown、onPointerMove、onPointerUp。

Listener(
  child: Container(
    alignment: Alignment.center,
    color: Colors.blue,
    width: 100,
    height: 100,
  ),
  onPointerDown: (event) => print('onPointerDown'),
  onPointerMove: (event) => print('onPointerMove'),
  onPointerUp: (event) => print('onPointerUp'),
),

Listener的behavior参数

// How to behave during hit tests.
enum HitTestBehavior {
  // Targets that defer to their children receive events within their bounds
  // only if one of their children is hit by the hit test.
  deferToChild,

  // Opaque targets can be hit by hit tests, causing them to both receive
  // events within their bounds and prevent targets visually behind them from
  // also receiving events.
  opaque,

  // Translucent targets both receive events within their bounds and permit
  // targets visually behind them to also receive events.
  translucent,
}

首先要了解两个概念 一个是渲染层级,还有一个是Widget的树结构。当手指点击之后会先通过渲染层级和behavior参数确定HitTest命中的组件,然后根据树结构依次向上传递Pointer Event。透明Widget一般HitTest都会失败,但是behavior参数可以依照如下说明进行设定。
deferToChild:当子节点widget的HitTest命中测试成功时,该节点一定会响应。
opaque:将当前节点Widget当作是不透明的进行处理(就算是透明的组件)。
translucent:透传,正常情况下HitTest只会响应渲染层级上面的组件。
IgnorePointer组件 被IgnorePointer组件包裹的子树及其本身都不会处理PointerEvent事件。
AbsorbPointer组件 被AbsorbPointer组件包裹的子树不会处理PointerEvent事件,但是AbsorbPointer组件本身会处理。

GestureDetector组件

GestureDetector组件对基本的PointerEvent事件进行了语义封装,通过GestureRecognizer使用Listener将PointerEvent转义成onTap、onDoubleTap、onLongPress、onPanDown、onVeticalDragUpdate等等接口。
Arena: Flutter定义了一个Arena手势竞技场,对有冲突的手势进行判断最后选出唯一的一个获胜者并处理事件。例如水平和垂直的ListView会根据横竖的滑动分量进行判断。
Tip: 手势冲突只是手势级别的,而手势是对原始指针的语义化的识别,所以在遇到复杂的冲突场景时,都可以通过Listener直接识别原始指针事件来解决冲突。

Notification

NotificationListener组件可以用来监听从子结构传递过来的通知,和手势通知不同,这种Notification可以选择是否还要往上传递。 常用的是ScrollStartNotification、ScrollUpdateNotification等滑动通知,也可以自定义通知dispatch向上分发。

你可能感兴趣的:(Flutter踩坑系列:滑动手势监听不到)