几乎每个应用程序都需要某种用户输入。通常,你的应用需要响应触摸事件。这是一个触摸事件的小指南,特别是对于启动Flutter开发人员。
在手势识别和对触摸事件的反应方面,Flutter为新开发人员提供了令人难以置信的小部件。你可以使应用中的任何微件响应触摸事件,只需将其包装在这些触摸识别小部件之一中即可。
无论您是刚刚了解有关Flutter上触摸事件管理的更多信息的开发人员,还是希望快速回顾的专业人士,本文都将帮助您快速掌握。
手势系统如何运作?
从本质上讲,Flutter中的手势系统在两个不同的层上运行。第一层存储描述物理细节的原始指针事件,例如正在使用的指针的位置和移动,例如测针、鼠标和触摸。
在此之上,我们有“手势”层。这会将指针移动解释为有意义的语义,并向指针移动添加上下文。
面向初学者的快速触摸事件词汇表
除非您了解语义手势和指针,否则无法处理触摸事件。指针是这个竞技场中的英雄!您将在下面看到这是如何实现的。
在我们探讨单个元素的反应之前,让我们看一下案例中涉及的特征:
指针
指针表示与用户与相关屏幕的交互相关的原始数据。有四种不同类型的指针事件:
PointerUpEvent: 指针已停止与屏幕交互。
PointerDownEvent:指针在特定位置与屏幕接触。
PointerMoveEvent:指针已移动到屏幕上的新位置。
PointerCancelEvent:此指针的输入不再指向正在进行的应用程序。
当您使用 时,框架将检查应用程序,以确定位于应用程序线框上该特定点的小部件。将调度到该小部件及其上方的小部件到组件树的根,在该特定路径上跟随 down 事件的任何事件也是如此。
手势
如前所述,手势表示语义操作(如点击、缩放和拖动),这些操作很容易从指针活动中出现的多个单独事件中识别出来。单个手势可能包含多个触摸事件,每个事件都处理手势的唯一里程碑,如拖动开始或拖动结束!
以下是 Flutter 触控事件管理中最常见的手势类型:
点按手势
onTapUp:指针会触发在屏幕上的特定位置的点击。
onTapDown:指针可能会导致在屏幕上联系过的点击。
onTap:当先前触发的指针也触发 时,它会导致点击。
onTapCancel:当指针触发不会导致点击时。
双击手势
onDoubleTap:用户快速连续点击同一位置的屏幕两次。
长按手势
onLongPress:指针与同一位置的屏幕保持接触的时间较长。
垂直拖动手势
onVerticalDragStart:指针接触屏幕,可能会垂直移动。
onVerticalDragEnd: 先前在垂直移动期间与屏幕接触的指针以与运动本身类似的速度结束与屏幕的接触。
onVerticalDragUpdate:与屏幕接触的指针沿同一方向垂直移动。
水平拖动手势
onHorizontalDragStart:指针接触屏幕,可能沿水平方向移动。
onHorizontalDragEnd:以前以特定速度水平移动以与屏幕接触的指针会完全失去接触。
onHorizontalDragUpdate:与屏幕接触的指针从onHorizontalDragStart水平移动。
平移手势
onPanStart:指针已接触屏幕,可能会在 X 轴或 Y 轴上移动。此回调在设置或已设置时导致崩溃 - 事件系统无法区分平移和拖动。
onPanEnd:与屏幕接触并以前以特定速度移动的指针会失去与屏幕的联系。
onPanUpdate:当前与屏幕接触并在水平或垂直方向上移动的指针。仅当设置了回调时,才会遇到崩溃。
了解手势消歧义
多个手势检测器可以出现在屏幕上的任何给定位置。它们都遵循流经的指针事件流的提示。在尝试识别特定手势时,小部件确定指定的语义手势,并根据其设置回调。因此,如果您在 上设置了某个回调,则小部件将尝试根据该类型的手势来解释指针事件。GestureDetectorGestureDetector
该框架消除了手势领域中的手势的歧义。每种类型的手势都有一个识别器,该识别器在竞技场中竞争,检查指针位置和传入的移动事件。与输入手势对应的识别器通过遵循以下几个规则获胜:
识别器可以随时宣布失败并离开竞技场。当只剩下一个识别器时,它就赢了!
识别器可以随时宣布胜利,并迫使其他人声称失败。
例如,假设您要识别垂直和水平拖动。每个方向的识别器将在收到第一个事件后立即进入手势竞技场。然后,他们将根据逻辑像素在任何给定方向上观察运动。
如果 move 事件主要垂直进行,则垂直识别器将由于交叉的逻辑像素而宣布胜利,并且手势将被解释为屏幕上的垂直拖动。对于水平拖动,也可以重复相同的一系列事件。
如果您只想识别水平拖动,那么运动是否有垂直组件并不重要;只有水平运动才会被识别。
在Flutter中编码触摸事件处理
在 Android 中工作时,您可以通过OnClicksetOnClickListener绑定来响应按钮点击。但是当涉及到Flutter时,您可以选择两种不同的方法来添加触摸听者。
如果小部件支持使用侦听器,则可以传递回调函数来处理事件。下面是代码示例:
onPressedRaisedButton
class BodyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return RaisedButton(
onPressed: (){
print("Click!");
},
child: Align(
alignment: Alignment.center,
child: new Text("Button", softWrap: true)
),
);
}
}
手势检测器示例
但是,您通常希望使用小部件尚不支持的侦听器。当小组件不直接支持侦听器时,您可以将该小组件包装在中,并将处理程序传递给参数。操作方法如下:
class SampleApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Flutter_App(),
);
}
}
class Flutter_App extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _State();
}
}
class _State extends State<Flutter_App>
with SingleTickerProviderStateMixin {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter App',
theme: ThemeData(primaryColor: Colors.blue),
home: Scaffold(
appBar: AppBar(
title: Text('Flutter App'),
),
body: Center(
child: GestureDetector(
child: FlutterLogo(
size: 200.0,
),
onTap: () {
print("Tap");
},
),
)));
}
@override
void dispose() {
// Resource release
super.dispose();
}
}
想知道如何收听双击事件吗?操作方法如下:
class SampleApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Sample App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: AnimateApp(),
);
}
}
class AnimateApp extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _AnimateAppState();
}
}
class _AnimateAppState extends State<AnimateApp>
with SingleTickerProviderStateMixin {
AnimationController controller;
Animation<double> animation;
CurvedAnimation curve;
@override
void initState() {
super.initState();
// Create AnimationController object
controller = AnimationController(
vsync: this, duration: const Duration(milliseconds: 2000));
curve = CurvedAnimation(parent: controller, curve: Curves.easeIn);
// Create Animation object through Tween object
animation = Tween(begin: 50.0, end: 200.0).animate(controller)
..addListener(() {
// Note: This sentence cannot be omitted, otherwise the widget will not be redrawn and the animation effect will not be seen
setState(() {});
});
// Perform animation
controller.forward();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'AnimateApp',
theme: ThemeData(primaryColor: Colors.blue),
home: Scaffold(
appBar: AppBar(
title: Text('AnimateApp'),
),
body: Center(
child: GestureDetector(
child: RotationTransition(
turns: curve,
child: FlutterLogo(
size: 200.0,
)),
onDoubleTap: () {
if (controller.isCompleted) {
controller.reverse();
} else {
controller.forward();
}
},
),
)));
}
@override
void dispose() {
// Resource release
controller.dispose();
super.dispose();
}
}
技术交流可以加Q群。