Skip to main content

点击、拖动和其他手势

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

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

指针

#

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

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

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

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

手势

#

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

点击

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

双击

onDoubleTap
用户快速连续两次点击屏幕上的同一位置。

长按

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

垂直拖动

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

水平拖动

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

平移

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

向 widget 添加手势检测

#

要从 widget 层监听手势,请使用GestureDetector

如果您使用的是 Material 组件,则许多这些 widget 已经响应点击或手势。例如,IconButtonTextButton 响应按下(点击),而 ListView 响应滑动以触发滚动。如果您没有使用这些 widget,但想要在点击时使用“墨水飞溅”效果,则可以使用InkWell

手势消除歧义

#

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

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

所有这些手势检测器都会监听指针事件流,因为它们会流过并尝试识别特定手势。GestureDetector widget 根据其哪些回调非空来决定尝试识别哪些手势。

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

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

  • 任何时候,识别器都可以声明自己是获胜者,导致所有剩余的识别器都失败。

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

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