写点什么

flutter 系列之: 移动端的手势基础 GestureDetector

作者:程序那些事
  • 2022 年 6 月 08 日
  • 本文字数:3782 字

    阅读完需:约 12 分钟

flutter系列之:移动端的手势基础GestureDetector

简介

移动的和 PC 端有什么不同呢?同样的 H5 可以运行在 APP 端,也可以运行在 PC 端。两者最大的区别就是移动端可以用手势。手势可以做到一些比如左滑右滑,上滑下滑,缩放等操作。


原生的 andorid 和 IOS 当然可以做到这些事情,作为一个移动的的开发框架 flutter,自然也能够支持手势。flutter 中的手势支持叫做 GestureDetector,一起来看看 flutter 中的手势基础吧。

Pointers 和 Listener

我们先来考虑一下最简单的手势是什么呢?很明显,最简单的手势就是模拟鼠标的点击操作。我们可以将其称之为 Pointer event,也就是各种点击事件。


flutter 中有四种 Pointer 事件,这些事件如下所示:


  • PointerDownEvent --表示用手点击了屏幕,接触到了一个 widget。

  • PointerMoveEvent --表示手指从一个位置移动到另外一个位置。

  • PointerUpEvent --手指从点击屏幕变成了离开屏幕。

  • PointerCancelEvent --表示手指离开了该应用程序。


那么点击事件的传递机制是什么样的呢?


以手指点击屏幕的 PointerDownEvent 事件为例,当手指点击屏幕的时候,flutter 首先会去定位该点击位置存在的 widget,然后将该点击事件传递给该位置的最小 widget.


然后点击事件从最新的 widget 向上开始冒泡,并将其分派到从最里面的 widget 到树根的路径上的所有 widget 中。


注意,flutter 中并没有取消或停止进一步分派 Pointer 事件的机制。


要想监听这写 Pointer 事件,最简单直接的办法就是使用 Listener:


class Listener extends SingleChildRenderObjectWidget {  /// Creates a widget that forwards point events to callbacks.  ///  /// The [behavior] argument defaults to [HitTestBehavior.deferToChild].  const Listener({    Key? key,    this.onPointerDown,    this.onPointerMove,    this.onPointerUp,    this.onPointerHover,    this.onPointerCancel,    this.onPointerSignal,    this.behavior = HitTestBehavior.deferToChild,    Widget? child,  }) : assert(behavior != null),       super(key: key, child: child);
复制代码


可以看到 Listener 也是一种 widget,并且可以监听多种 Pointer 的事件。


我们可以把要监听 Pointer 的 widget 封装在 Listener 中,这样就可以监听各种 Pointer 事件了,具体的例子如下:


Widget build(BuildContext context) {    return ConstrainedBox(      constraints: BoxConstraints.tight(const Size(300.0, 200.0)),      child: Listener(        onPointerDown: _incrementDown,        onPointerMove: _updateLocation,        onPointerUp: _incrementUp,        child: Container(          color: Colors.lightBlueAccent,          child: Column(            mainAxisAlignment: MainAxisAlignment.center,            children: <Widget>[              const Text(                  'You have pressed or released in this area this many times:'),              Text(                '$_downCounter presses\n$_upCounter releases',                style: Theme.of(context).textTheme.headline4,              ),              Text(                'The cursor is here: (${x.toStringAsFixed(2)}, ${y.toStringAsFixed(2)})',              ),            ],          ),        ),      ),    );
void _incrementDown(PointerEvent details) { _updateLocation(details); setState(() { _downCounter++; }); }
void _incrementUp(PointerEvent details) { _updateLocation(details); setState(() { _upCounter++; }); }
void _updateLocation(PointerEvent details) { setState(() { x = details.position.dx; y = details.position.dy; }); }
复制代码


但是对于 Lisenter 来说只能监听最原始的 Pointer 事件,所以如果想监听更多类型的手势事件的话,则可以使用 GestureDetector.

GestureDetector

GestureDetector 可以检测下面这些手势,包括:


  1. Tap


Tap 表示的是用户点击的事件,Tap 有下面几种事件:


onTapDownonTapUponTaponTapCancel
复制代码


  1. Double tap


Double tap 表示的是双击事件,Double tap 只有一种类型:


onDoubleTap
复制代码


  1. Long press


Long press 表示的是长按。也只有下面一种类型:


onLongPress
复制代码


  1. Vertical drag


Vertical drag 表示的是垂直方向的拉,它有三个事件,分别是:


onVerticalDragStartonVerticalDragUpdateonVerticalDragEnd
复制代码


  1. Horizontal drag


有垂直方向的拉,就有水平方向的拉,Horizontal drag 表示的是水平方向的拉,它同样有三个事件,分别是:


onHorizontalDragStartonHorizontalDragUpdateonHorizontalDragEnd
复制代码


  1. Pan


Pan 这个东西可以看做是 Vertical drag 和 Horizontal drag 的合集, 因为有时候我们是希望同时可以水平或者垂直移动,在这种情况下面,我们就需要使用到 Pan 的事件:


onPanStartonPanUpdateonPanEnd
复制代码


注意, Pan 是和单独的 Vertical drag、Horizontal drag 是相互冲突的,不能同时使用。


要想监听上面的这些事件,我们可以使用 GestureDetector,先看下 GestureDetector 的定义:


class GestureDetector extends StatelessWidget {  GestureDetector({    Key? key,    this.child,    this.onTapDown,    this.onTapUp,    this.onTap,    this.onTapCancel,    this.onSecondaryTap,    this.onSecondaryTapDown,    this.onSecondaryTapUp,    this.onSecondaryTapCancel,    this.onTertiaryTapDown,    this.onTertiaryTapUp,    this.onTertiaryTapCancel,    this.onDoubleTapDown,    this.onDoubleTap,    this.onDoubleTapCancel,    this.onLongPressDown,    this.onLongPressCancel,    this.onLongPress,    this.onLongPressStart,    this.onLongPressMoveUpdate,    this.onLongPressUp,    this.onLongPressEnd,    this.onSecondaryLongPressDown,    this.onSecondaryLongPressCancel,    this.onSecondaryLongPress,    this.onSecondaryLongPressStart,    this.onSecondaryLongPressMoveUpdate,    this.onSecondaryLongPressUp,    this.onSecondaryLongPressEnd,    this.onTertiaryLongPressDown,    this.onTertiaryLongPressCancel,    this.onTertiaryLongPress,    this.onTertiaryLongPressStart,    this.onTertiaryLongPressMoveUpdate,    this.onTertiaryLongPressUp,    this.onTertiaryLongPressEnd,    this.onVerticalDragDown,    this.onVerticalDragStart,    this.onVerticalDragUpdate,    this.onVerticalDragEnd,    this.onVerticalDragCancel,    this.onHorizontalDragDown,    this.onHorizontalDragStart,    this.onHorizontalDragUpdate,    this.onHorizontalDragEnd,    this.onHorizontalDragCancel,    this.onForcePressStart,    this.onForcePressPeak,    this.onForcePressUpdate,    this.onForcePressEnd,    this.onPanDown,    this.onPanStart,    this.onPanUpdate,    this.onPanEnd,    this.onPanCancel,    this.onScaleStart,    this.onScaleUpdate,    this.onScaleEnd,    this.behavior,    this.excludeFromSemantics = false,    this.dragStartBehavior = DragStartBehavior.start,  })
复制代码


可以看到 GestureDetector 是一个无状态的 Widget,它和 Listner 一样,可以接受一个 child Widget,然后监听了很多手势的事件。


所以, 一般来说,我们这样来使用它:


GestureDetector(              onTap: () {                setState(() {                  // Toggle light when tapped.                  _lightIsOn = !_lightIsOn;                });              },              child: Container(                color: Colors.yellow.shade600,                padding: const EdgeInsets.all(8),                // Change button text when light changes state.                child: Text(_lightIsOn ? 'TURN LIGHT OFF' : 'TURN LIGHT ON'),              ),            ),
复制代码


注意, 如果 GestureDetector 中有 child,那么 onTap 的作用范围就在子 child 的范围。如果 GestureDetector 中并没有 child,那么其作用范围就是 GestureDetector 的父 widget 的范围。

手势冲突

因为手势的监听有很多种方式,但是这些方式并不是完全独立的,有时候这些手势可能是互相冲突的。比如前面我们提到的 Pan 和 Vertical drag、Horizontal drag。


如果遇到这样的情况,那么 futter 会自行进行冲突解决,去选择到底用户执行的是哪个操作。


比如,当用户同时进行水平和垂直拖动的时候,两个识别器在接收到指针向下事件时都会开始观察指针移动事件。


如果指针水平移动超过一定数量的逻辑像素,则水平识别器获胜,然后将该手势解释为水平拖动。 类似地,如果用户垂直移动超过一定数量的逻辑像素,则垂直识别器获胜。

总结

手势识别是移动端的优势项目,大家可以尝试在需要的地方使用 GestureDetector,可以达到意想不到的用户效果哦。


更多内容请参考 http://www.flydean.com/05-flutter-gestures/

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

发布于: 19 小时前阅读数: 14
用户头像

关注公众号:程序那些事,更多精彩等着你! 2020.06.07 加入

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧,尽在公众号:程序那些事!

评论

发布
暂无评论
flutter系列之:移动端的手势基础GestureDetector_flutter_程序那些事_InfoQ写作社区