【交互 widget】Flutter GestureDetector

437 阅读2分钟

一个能识别手势的 widget。相比于 Listener,GestureDetector,更加常用。

GestureDetector 介绍

GestureDetector 就不分析源码了,有兴趣的同学可以读下这篇 从源码看flutter(五):GestureDetector篇。简单的介绍下,GestureDetector 是一个 StateFulWidget,内部调用 RawGestureDetector,RawGestureDetector 又调用 Listener 监听 onPointerDown 和 onPointerPanZoomStart 事件。GestureDetector 作用仅是简化 RawGestureDetector 的使用。作为平时使用 GestureDetector 的功能就足够了。哪天如果不能满足的时候,再去找 RawGestureDetector。

使用 GestureDetector

GestureDetector 使用了很多手势识别器,最常用的就是 TapGestureRecognizer 了,使用起来很简单。

GestureDetector(
 onTap: () {
   print('tap');
 },
 child: Container(
   width: 100,
   height: 100,
   color: Colors.blue,
 ))

如果没有 child 要响应 pointer 事件,需要指定 behavior。

GestureDetector(
    behavior: HitTestBehavior.opaque,
    onTap: () {
      debugPrint('tap');
    },
 );

opaque 的含义在 Listener 中有讲。

其它手势也都一样,使用还是很简单的,真正需要我们关心的是手势冲突。

有多个手势竞争的时候,child 的手势会胜出

GestureDetector(
      onTap: () {
        debugPrint('tap parent');
      },
      child: GestureDetector(
        behavior: HitTestBehavior.opaque,
        onTap: () {
          debugPrint('tap child');
        },
      ),
    );

只输出 tap child。 当 parent 和 child 同时响应 onTap 时, child 胜出。当有多个手势参与竞争的时候,需要竞技场管理者进行裁决。当 pointer up 事件发出的时候,竞技场管理者判定第一个位置的 tap 手势选手胜出。在执行 hitTest 的时候,是深度优先,所以 child 会先加入 结果列表,位置一定在 parent 前面。

pointer move 事件会打断 tap

GestureDetector(
      behavior: HitTestBehavior.opaque,
      onHorizontalDragEnd: (details) {
        debugPrint('darg end');
      },
      onTapDown: (details) {
        debugPrint('tap down');
      },
      onTap: () {
        debugPrint('tap');
      },
    );

如果有滑动手势胜出,输出 “tap down” 和 “move end”。

pointer down 后在一定时间内滑动超过一定距离,那么就会被判定为 PointerMoveEvent,tap 手势被移出竞技场。tapUp 和 tap 事件不会再响应。tapDown 会响应,因为 tapDown 在 pointerDownEvent 的时候就被裁决出来了。

同时存在 tap ,doubleTap

只有 tap 的时候,pointer up 的时候,立即执行,如果同时存在 double tap,需要等上 300ms,因为要判断是否要响应 double tap。tap ,doubleTap 只能有一个胜出。

同时存在 tap ,onLongPress

如果同时存在 tap,onLongPress,需要等上 500ms,因为要判断是否要响应 longPress。tap ,onLongPress 只能有一个胜出。

虽然只举这几个例子,但当多个手势竞争的时候必然会有冲突,解决冲突可以用 Listener 替代。