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

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

指针

#

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

PointerDownEvent
指针在特定位置接触屏幕。
PointerMoveEvent
指针已从屏幕上的一个位置移动到另一个位置。
PointerUpEvent
指针已停止接触屏幕。
PointerCancelEvent
此指针的输入不再指向此应用。

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

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

手势

#

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

点击

onTapDown
可能导致点击的指针已在特定位置接触屏幕。
onTapUp
触发点击的指针已在特定位置停止接触屏幕。
onTap
之前触发 onTapDown 的指针也触发了 onTapUp,最终导致了一次轻触。
onTapCancel
之前触发 onTapDown 的指针最终不会导致轻触。

双击

onDoubleTap
用户在同一位置快速连续轻触屏幕两次。

长按

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

垂直拖动

onVerticalDragStart
指针已接触屏幕并可能开始垂直移动。
onVerticalDragUpdate
一个接触屏幕并垂直移动的指针已在垂直方向移动。
onVerticalDragEnd
之前与屏幕接触并垂直移动的指针不再与屏幕接触,并且在停止接触屏幕时以特定速度移动。

水平拖动

onHorizontalDragStart
指针已接触屏幕并可能开始水平移动。
onHorizontalDragUpdate
一个接触屏幕并水平移动的指针已在水平方向移动。
onHorizontalDragEnd
一个之前接触屏幕并水平移动的指针不再接触屏幕,并在停止接触屏幕时以特定速度移动。

平移

onPanStart
指针已接触屏幕并可能开始水平或垂直移动。如果设置了 onHorizontalDragStartonVerticalDragStart,此回调将崩溃。
onPanUpdate
一个接触屏幕并在垂直或水平方向移动的指针。如果设置了 onHorizontalDragUpdateonVerticalDragUpdate,此回调将崩溃。
onPanEnd
一个之前接触屏幕的指针不再接触屏幕,并在停止接触屏幕时以特定速度移动。如果设置了 onHorizontalDragEndonVerticalDragEnd,此回调将崩溃。

为小部件添加手势检测

#

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

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

手势消歧

#

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

  • 一个 ListTile 有一个响应整个 ListTile 的轻触识别器,以及一个围绕尾部图标按钮的嵌套识别器。尾部图标的屏幕区域现在被两个手势识别器覆盖,如果手势最终被识别为轻触,它们需要协商由谁来处理。
  • 一个 GestureDetector 覆盖了一个配置为处理多个手势(例如长按和轻触)的屏幕区域。当用户触摸屏幕的该部分时,轻触识别器现在必须与 长按识别器协商。根据该指针接下来发生的情况,两个识别器之一会接收到手势,或者如果用户执行的既不是轻触也不是长按的操作,则两个识别器都不会接收到手势。

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

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

  • 在任何时候,识别器都可以自行退出竞技场。如果竞技场中只剩下一个识别器,该识别器获胜。

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

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

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