跳到主内容

点击、拖动和其他手势

在 Flutter 中,点击和拖动等手势的工作原理。

本文档解释了如何在 Flutter 中监听和响应手势。手势的例子包括点击、拖动和缩放。

Flutter 中的手势系统具有两个独立的层级。第一层具有原始指针事件,这些事件描述了指针(例如触摸、鼠标和触控笔)在屏幕上的位置和移动。第二层具有手势,这些手势描述了语义操作,这些操作由一个或多个指针移动组成。

指针

#

指针代表用户与设备屏幕交互的原始数据。有四种类型的指针事件

PointerDownEvent

指针已在特定位置与屏幕接触。

PointerMoveEvent

指针已从屏幕上的一个位置移动到另一个位置。

PointerUpEvent

指针已停止与屏幕接触。

PointerCancelEvent

来自此指针的输入不再定向到此应用。

在指针按下时,框架会对您的应用执行命中测试,以确定指针与屏幕接触的位置上存在哪个小部件。然后,指针按下事件(以及该指针的后续事件)将被分派到命中测试找到的最内层小部件。从那里,事件会冒泡到树中,并被分派到从最内层小部件到树根路径上的所有小部件。没有机制可以取消或阻止指针事件进一步分派。

要直接从小部件层级监听指针事件,请使用 Listener 小部件。但是,通常,请考虑使用手势(如下所述)。

手势

#

手势代表从多个单独的指针事件(甚至多个单独的指针)识别出的语义操作(例如,点击、拖动和缩放)。手势可以分派多个事件,对应于手势的生命周期(例如,拖动开始、拖动更新和拖动结束)。

点击

onTapDown

可能导致点击的指针已在特定位置接触屏幕。

onTapUp

触发点击的指针已在特定位置停止接触屏幕。

onTap

先前触发 onTapDown 的指针也触发了 onTapUp,最终导致了点击。

onTapCancel

先前触发 onTapDown 的指针不会导致点击。

双击

onDoubleTap

用户在短时间内在屏幕上的相同位置点击了两次。

长按

onLongPress

指针在同一位置长时间保持与屏幕接触。

垂直拖动

onVerticalDragStart

指针已接触屏幕并可能开始垂直移动。

onVerticalDragUpdate

与屏幕接触并垂直移动的指针已在垂直方向上移动。

onVerticalDragEnd

之前与屏幕接触并垂直移动的指针不再与屏幕接触,并且在停止接触屏幕时以特定速度移动。

水平拖动

onHorizontalDragStart

指针已接触屏幕并可能开始水平移动。

onHorizontalDragUpdate

与屏幕接触并水平移动的指针已在水平方向上移动。

onHorizontalDragEnd

先前与屏幕接触并水平移动的指针不再与屏幕接触,并且在停止与屏幕接触时以特定速度移动。

Pan

onPanStart

指针已与屏幕接触,并且可能开始水平或垂直移动。如果设置了 onHorizontalDragStartonVerticalDragStart,则此回调会崩溃。

onPanUpdate

与屏幕接触并正在垂直或水平方向上移动的指针。如果设置了 onHorizontalDragUpdateonVerticalDragUpdate,则此回调会崩溃。

onPanEnd

先前与屏幕接触的指针不再与屏幕接触,并且在停止与屏幕接触时以特定速度移动。如果设置了 onHorizontalDragEndonVerticalDragEnd,则此回调会崩溃。

为小部件添加手势检测

#

要从小部件层级监听手势,请使用 GestureDetector

如果您正在使用 Material Components,许多这些小部件已经响应点击或手势。例如,IconButtonTextButton 响应按下操作(点击),而 ListView 响应滑动以触发滚动。如果您没有使用这些小部件,但希望在点击时获得“墨水飞溅”效果,可以使用 InkWell

手势消歧

#

在屏幕上的给定位置,可能有多个手势检测器。例如

  • ListTile 具有响应整个 ListTile 的点击识别器,以及嵌套在尾部图标按钮周围的一个识别器。尾部图标的屏幕矩形现在被两个需要协商谁处理手势(如果碰巧是点击)的手势识别器覆盖。
  • 单个 GestureDetector 覆盖屏幕区域,配置为处理多种手势,例如长按和点击。tap 识别器现在必须与 long press 识别器协商,当用户触摸屏幕的该部分时。根据指针的后续操作,两个识别器中的一个接收手势,或者如果用户执行既不是点击也不是长按的操作,则两者都不接收手势。

所有这些手势检测器都会监听指针事件流,并尝试识别特定的手势。 GestureDetector 小部件会根据其哪些回调不为空来决定尝试识别哪些手势。

当屏幕上存在多个手势识别器时,框架通过让每个识别器加入手势竞技场来消除歧义,以确定用户打算使用哪个手势。手势竞技场使用以下规则来确定哪个手势获胜

  • 任何时候,识别器都可以消除自身并离开竞技场。如果竞技场中只剩下一个识别器,则该识别器获胜。

  • 任何时候,识别器都可以宣布自己获胜,导致所有剩余识别器失败。

例如,在消除水平和垂直拖动歧义时,当它们收到指针按下事件时,两个识别器都会进入竞技场。识别器会观察指针移动事件。如果用户水平移动指针超过一定数量的逻辑像素,则水平识别器宣布获胜,手势将被解释为水平拖动。同样,如果用户垂直移动超过一定数量的逻辑像素,则垂直识别器宣布自己获胜。

当只有一个水平(或垂直)拖动识别器时,手势竞技场是有益的。在这种情况下,竞技场中只有一个识别器,并且水平拖动会立即被识别,这意味着水平移动的第一个像素可以被视为拖动,并且用户无需等待进一步的手势消除歧义。