logo头像
Snippet 博客主题

Flutter 手势交互

本文于325天之前发表,文中内容可能已经过时。

Flutter 手势交互

参考

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

  • PointerDownEvent 指针接触到屏幕的特定位置
  • PointerMoveEvent 指针从屏幕上的一个位置移动到另一个位置
  • PointerUpEvent 指针停止接触屏幕
  • PointerCancelEvent 指针的输入事件不再针对此应用(事件取消)

在指针按下时,框架对您的应用程序执行命中测试,以确定指针与屏幕相接的位置存在哪些widget。 指针按下事件(以及该指针的后续事件)然后被分发到由命中测试发现的最内部的widget。 从那里开始,这些事件会冒泡在widget树中向上冒泡,这些事件会从最内部的widget被分发到到widget根的路径上的所有小部件(译者语:这和web中事件冒泡机制相似), 没有机制取消或停止冒泡过程(译者语:这和web不同,web中的时间冒泡是可以停止的)。

To listen to pointer events directly from the widgets layer, use a Listener widget. However, generally, consider using gestures (as discussed below) instead. 要直接从widget层监听指针事件,可以使用Listenerwidget。 但是,通常,请考虑使用手势(如下所述)

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

Tap

  • onTapDown 指针已经在特定位置与屏幕接触
  • onTapUp 指针停止在特定位置与屏幕接触
  • onTap tap事件触发
  • onTapCancel 先前指针触发的onTapDown不会在触发tap事件

双击

  • onDoubleTap 用户快速连续两次在同一位置轻敲屏幕.

长按

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

垂直拖动

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

水平拖动

  • onHorizontalDragStart 指针已经接触到屏幕并可能开始水平移动
  • onHorizontalDragUpdate 指针与屏幕接触并已沿水平方向移动
  • onHorizontalDragEnd 先前与屏幕接触并水平移动的指针不再与屏幕接触,并在停止接触屏幕时以特定速度移动
    要从widget层监听手势,请使用 GestureDetector.

如果您使用的是Material Components,那么大多数widget已经对tap或手势做出了响应。 例如 IconButton和 FlatButton 响应presses(taps),ListView响应滑动事件触发滚动。 如果您不使用这些widget,但想要在点击时上出现“墨水飞溅”效果,可以使用InkWell。

手势消歧
在屏幕上的指定位置,可能会有多个手势检测器。所有这些手势检测器在指针事件流经过并尝试识别特定手势时监听指针事件流。 GestureDetectorwidget决定是哪种手势。

当屏幕上给定指针有多个手势识别器时,框架通过让每个识别器加入一个“手势竞争场”来确定用户想要的手势。“手势竞争场”使用以下规则确定哪个手势胜出

在任何时候,识别者都可以宣布失败并离开“手势竞争场”。如果在“竞争场”中只剩下一个识别器,那么该识别器就是赢家

在任何时候,识别者都可以宣布胜利,这会导致胜利,并且所有剩下的识别器都会失败

例如,在消除水平和垂直拖动的歧义时,两个识别器在接收到指针向下事件时进入“手势竞争场”。识别器观察指针移动事件。 如果用户将指针水平移动超过一定数量的逻辑像素,则水平识别器将声明胜利,并且手势将被解释为水平拖拽。 类似地,如果用户垂直移动超过一定数量的逻辑像素,垂直识别器将宣布胜利。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import 'package:flutter/material.dart';

void main() {
runApp(new MaterialApp(
title: 'Flutter教程',
home: new MyButton(),
));
}

class MyButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new GestureDetector(
//Tap

// onTapDown 指针已经在特定位置与屏幕接触
onTapDown: (tapDownDetails) {
print('MyButton被监听了! onTapDown' + tapDownDetails.toString());
},

// onTapUp 指针停止在特定位置与屏幕接触
onTapUp: (tapDownDetails) {
print('MyButton被监听了! onTapUp' + tapDownDetails.toString());
},

// onTap tap事件触发
onTap: () {
print('MyButton被监听了! onTap');
},

// onTapCancel 先前指针触发的onTapDown不会在触发tap事件
onTapCancel: () {
print('MyButton被监听了! onTapCancel');
},



// 双击
// onDoubleTap 用户快速连续两次在同一位置轻敲屏幕.
onDoubleTap: () {
print('MyButton被监听了! onDoubleTap');
},

// 长按
// onLongPress 指针在相同位置长时间保持与屏幕接触
onLongPress: () {
print('MyButton被监听了! onLongPress');
},



// 垂直拖动
// onVerticalDragStart 指针已经与屏幕接触并可能开始垂直移动
onVerticalDragStart: (tapDownDetails) {
print('MyButton被监听了! onVerticalDragStart');
},


// onVerticalDragUpdate 指针与屏幕接触并已沿垂直方向移动.
onVerticalDragUpdate: (tapDownDetails) {
print('MyButton被监听了! onVerticalDragUpdate');
},

// onVerticalDragEnd 先前与屏幕接触并垂直移动的指针不再与屏幕接触,并且在停止接触屏幕时以特定速度移动
onVerticalDragEnd: (tapDownDetails) {
print('MyButton被监听了! onVerticalDragEnd');
},

// 水平拖动
// onHorizontalDragStart 指针已经接触到屏幕并可能开始水平移动
onHorizontalDragStart: (tapDownDetails) {
print('MyButton被监听了! onHorizontalDragStart');
},
// onHorizontalDragUpdate 指针与屏幕接触并已沿水平方向移动
onHorizontalDragUpdate: (tapDownDetails) {
print('MyButton被监听了! onHorizontalDragUpdate');
},
// onHorizontalDragEnd 先前与屏幕接触并水平移动的指针不再与屏幕接触,并在停止接触屏幕时以特定速度移动
onHorizontalDragEnd: (tapDownDetails) {
print('MyButton被监听了! onHorizontalDragEnd');
},


child: new Container(
height: 36.0,
padding: const EdgeInsets.all(8.0),
margin: const EdgeInsets.symmetric(horizontal: 8.0),
decoration: new BoxDecoration(
borderRadius: new BorderRadius.circular(5.0),
color: Colors.lightGreen[500],
),
child: new Center(
child: new Text('点击监听'),
),
),
);
}
}

Log

1
2
3
4
5
6
7
8
9
10
11
12
13
08-24 16:18:00.106 15230-15255/com.yourcompany.flutterdemo I/flutter: MyButton被监听了!   onVerticalDragUpdate
08-24 16:18:00.189 15230-15255/com.yourcompany.flutterdemo I/flutter: MyButton被监听了! onVerticalDragUpdate
08-24 16:18:00.199 15230-15255/com.yourcompany.flutterdemo I/flutter: MyButton被监听了! onVerticalDragEnd
08-24 16:25:52.034 15230-15255/com.yourcompany.flutterdemo I/flutter: MyButton被监听了! onTapCancel
08-24 16:25:52.038 15230-15255/com.yourcompany.flutterdemo I/flutter: MyButton被监听了! onVerticalDragStart
MyButton被监听了! onVerticalDragUpdate
08-24 16:25:52.134 15230-15255/com.yourcompany.flutterdemo I/flutter: MyButton被监听了! onVerticalDragUpdate
08-24 16:25:52.145 15230-15255/com.yourcompany.flutterdemo I/flutter: MyButton被监听了! onVerticalDragEnd
08-24 16:25:52.789 15230-15255/com.yourcompany.flutterdemo I/flutter: MyButton被监听了! onTapCancel
08-24 16:25:52.793 15230-15255/com.yourcompany.flutterdemo I/flutter: MyButton被监听了! onVerticalDragStart
MyButton被监听了! onVerticalDragUpdate
08-24 16:25:52.881 15230-15255/com.yourcompany.flutterdemo I/flutter: MyButton被监听了! onVerticalDragUpdate
08-24 16:25:52.891 15230-15255/com.yourcompany.flutterdemo I/flutter: MyButton被监听了! onVerticalDragEnd
支付宝打赏 微信打赏

打赏