点击、拖动和其他手势
本文档介绍了如何在 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
- 指针已接触屏幕,并且可能开始水平或垂直移动。如果设置了
onHorizontalDragStart
或onVerticalDragStart
,则此回调会崩溃。 onPanUpdate
- 与屏幕接触并在垂直或水平方向上移动的指针。如果设置了
onHorizontalDragUpdate
或onVerticalDragUpdate
,则此回调会崩溃。 onPanEnd
- 先前与屏幕接触的指针不再与屏幕接触,并且在停止接触屏幕时以特定速度移动。如果设置了
onHorizontalDragEnd
或onVerticalDragEnd
,则此回调会崩溃。
为 Widget 添加手势检测
#要从 Widget 层监听手势,请使用GestureDetector
。
如果您使用的是 Material Components,则许多这些 Widget 已经响应点击或手势。例如,IconButton
和 TextButton
响应按下(点击),以及 ListView
响应滑动以触发滚动。如果您未使用这些 Widget,但希望在点击时出现“墨水飞溅”效果,则可以使用 InkWell
。
手势消歧
#在屏幕上的给定位置,可能存在多个手势检测器。例如
- 一个
ListTile
有一个手势识别器,它响应整个ListTile
,以及围绕尾随图标按钮的嵌套识别器。尾随图标的屏幕矩形现在被两个手势识别器覆盖,如果结果是点击,则这两个手势识别器需要协商谁来处理手势。 - 单个
GestureDetector
覆盖一个配置为处理多个手势(例如长按和点击)的屏幕区域。当用户触摸屏幕的该部分时,点击
识别器现在必须与长按
识别器协商。根据接下来使用该指针发生的情况,两个识别器之一接收手势,或者如果用户执行既不是点击也不是长按的操作,则两个识别器都不接收手势。
所有这些手势检测器都在它们流过时监听指针事件流,并尝试识别特定手势。GestureDetector
Widget 根据其哪些回调非空来决定尝试识别哪些手势。
当屏幕上给定指针存在多个手势识别器时,框架通过让每个识别器加入手势竞技场来消除歧义用户意图的手势。手势竞技场使用以下规则确定哪个手势获胜
在任何时候,识别器都可以消除自身并离开竞技场。如果竞技场中只剩下一个识别器,则该识别器获胜。
在任何时候,识别器都可以宣布自己获胜,导致所有剩余的识别器失败。
例如,在消除水平和垂直拖动的歧义时,当它们收到指针按下事件时,两个识别器都会进入竞技场。识别器观察指针移动事件。如果用户在水平方向上移动指针超过一定数量的逻辑像素,则水平识别器声明获胜,并且手势被解释为水平拖动。类似地,如果用户在垂直方向上移动指针超过一定数量的逻辑像素,则垂直识别器会宣布自己获胜。
当只有一个水平(或垂直)拖动手势识别器时,手势竞技场非常有用。在这种情况下,竞技场中只有一个识别器,并且水平拖动会立即被识别,这意味着水平移动的第一个像素可以被视为拖动,并且用户无需等待进一步的手势消除歧义。
除非另有说明,否则本网站上的文档反映了 Flutter 的最新稳定版本。页面上次更新于 2024-07-06。 查看源代码 或 报告问题。