点击、拖动和其他手势
在 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-
指针已与屏幕接触,并且可能开始水平或垂直移动。如果设置了
onHorizontalDragStart或onVerticalDragStart,则此回调会崩溃。 onPanUpdate-
与屏幕接触并正在垂直或水平方向上移动的指针。如果设置了
onHorizontalDragUpdate或onVerticalDragUpdate,则此回调会崩溃。 onPanEnd-
先前与屏幕接触的指针不再与屏幕接触,并且在停止与屏幕接触时以特定速度移动。如果设置了
onHorizontalDragEnd或onVerticalDragEnd,则此回调会崩溃。
为小部件添加手势检测
#要从小部件层级监听手势,请使用 GestureDetector。
如果您正在使用 Material Components,许多这些小部件已经响应点击或手势。例如,IconButton 和 TextButton 响应按下操作(点击),而 ListView 响应滑动以触发滚动。如果您没有使用这些小部件,但希望在点击时获得“墨水飞溅”效果,可以使用 InkWell。
手势消歧
#在屏幕上的给定位置,可能有多个手势检测器。例如
ListTile具有响应整个ListTile的点击识别器,以及嵌套在尾部图标按钮周围的一个识别器。尾部图标的屏幕矩形现在被两个需要协商谁处理手势(如果碰巧是点击)的手势识别器覆盖。- 单个
GestureDetector覆盖屏幕区域,配置为处理多种手势,例如长按和点击。tap识别器现在必须与long press识别器协商,当用户触摸屏幕的该部分时。根据指针的后续操作,两个识别器中的一个接收手势,或者如果用户执行既不是点击也不是长按的操作,则两者都不接收手势。
所有这些手势检测器都会监听指针事件流,并尝试识别特定的手势。 GestureDetector 小部件会根据其哪些回调不为空来决定尝试识别哪些手势。
当屏幕上存在多个手势识别器时,框架通过让每个识别器加入手势竞技场来消除歧义,以确定用户打算使用哪个手势。手势竞技场使用以下规则来确定哪个手势获胜
-
任何时候,识别器都可以消除自身并离开竞技场。如果竞技场中只剩下一个识别器,则该识别器获胜。
-
任何时候,识别器都可以宣布自己获胜,导致所有剩余识别器失败。
例如,在消除水平和垂直拖动歧义时,当它们收到指针按下事件时,两个识别器都会进入竞技场。识别器会观察指针移动事件。如果用户水平移动指针超过一定数量的逻辑像素,则水平识别器宣布获胜,手势将被解释为水平拖动。同样,如果用户垂直移动超过一定数量的逻辑像素,则垂直识别器宣布自己获胜。
当只有一个水平(或垂直)拖动识别器时,手势竞技场是有益的。在这种情况下,竞技场中只有一个识别器,并且水平拖动会立即被识别,这意味着水平移动的第一个像素可以被视为拖动,并且用户无需等待进一步的手势消除歧义。