如何在Flutter中对触摸事件做出反应

384 阅读7分钟

几乎每个应用程序都需要某种用户输入。通常你的应用程序需要对触摸事件做出反应。这是一个关于触摸事件的小指南,特别是对于刚开始的Flutter开发者。

当涉及到手势识别和对触摸事件的反应时,Flutter为新的开发者提供了令人难以置信的小部件。你可以让你的应用程序中的任何部件对触摸事件做出反应,只需将其包裹在这些触摸识别部件中的一个。

无论您是刚刚了解Flutter上触摸事件管理的开发者,还是想快速回顾一下的专业人士,这篇文章都能让您快速了解。

手势系统是如何运作的?

基本上,Flutter 中的手势系统在两个不同的层面上运行。第一层存储原始的指针事件,描述物理细节,如使用中的指针的位置和移动,如测针、鼠标和一般的触控。

在这之上,我们有一个 "手势 "层。这将指针的运动解释为有意义的语义,并为指针的运动增加了一个背景。

初学者的快速触摸事件词汇

除非你了解语义手势和指针,否则无法处理触摸事件。指针是这个领域的英雄!你会看到这在字面上是如何实现的,如下。

在我们探讨个别元素的反应之前,让我们先看看案例中涉及的特征。

指针

指针代表了与用户与有关屏幕的互动有关的原始数据。有四种不同类型的指针事件。

  • PointerUpEvent:指 针已停止与屏幕的交互。
  • PointerDownEvent:指针在一个特定的位置上与屏幕接触。
  • PointerMoveEvent:指针已经移动到屏幕上的一个新位置。
  • PointerCancelEvent:这个指针的输入不再指向正在进行的应用程序。

当你参与PointerDownEvent ,框架将检查应用程序,以确定位于应用程序线框上该特定点的小部件。PointerDownEvent将被派发到该widget和它上面的widget到组件树的根部,任何跟随该特定路径上的向下事件的事件也会被派发。

手势

如前所述,手势代表了语义上的动作,如点击、缩放和拖动,这些动作很容易从一个指针活动中出现的多个单独事件中识别出来。一个手势可能包含多个触摸事件,每个事件都处理手势的一个独特的里程碑,如拖动开始或拖动结束!

以下是Flutter上触摸事件管理中涉及的最常见的手势类型。

点触手势

  • onTapUp:指针触发了对屏幕上某一特定位置的点击。
  • onTapDown:指针可能导致在屏幕上接触到一个点。
  • onTap:当指针先前触发了onTapDown ,也触发了onTapUp ,导致轻拍。
  • onTapCancel:当指针触发onTapDown ,不会引起轻拍。

双击手势

  • onDoubleTap:用户在同一位置快速连续敲击屏幕两次。

长按手势

  • onLongPress:指针在同一位置上与屏幕保持较长时间的接触。

垂直拖动手势

  • onVerticalDragStart:指针接触屏幕大概垂直移动。
  • onVerticalDragEnd:指 针在垂直移动过程中先前与屏幕接触,以与移动本身相似的速度结束与屏幕的接触。
  • onVerticalDragUpdate:与屏幕接触的指针在同一方向上垂直移动。

水平拖动手势

  • onHorizontalDragStart:指针与屏幕接触后有可能在水平方向上移动。
  • onHorizontalDragEnd:指针与屏幕接触后,以前以特定的速度在水平方向上移动,现在完全失去了接触。
  • onHorizontalDragUpdate:指针与屏幕接触后水平移动,从onHorizontalDragStart

平移手势

  • onPanStart: 指针已经接触到屏幕并可能在X或Y轴上移动。当onHorizontalDragStartonVerticalDragStart ,这个回调会导致崩溃--事件系统没有办法区分平移或拖动的区别。
  • onPanEnd指针与屏幕接触并以特定速度移动的指针失去了与屏幕的接触。当回调设置为onHorizontalDragEndonVerticalDragEnd 时,会导致崩溃。
  • onPanUpdate:指针目前与屏幕接触并以水平或垂直方向移动。只有在设置onHorizontalDragUpdateonVerticalDragUpdate 的回调时才会发生崩溃。

了解手势歧义

在屏幕上的任何给定位置都可能存在多个手势检测器。所有这些探测器都遵循流过的指针事件的线索。为了试图识别特定的手势,GestureDetector widget根据已经设置的回调来确定指定的语义手势。意思是说,如果你在GestureDetector 上设置了一个特定的回调,该小组件将试图根据该类型的手势来解释指针事件。

该框架在手势领域中对手势进行了区分。每种类型的手势都有一个识别器,在竞技场上竞争,检查进入的指针位置和运动事件。与输入手势相对应的识别器通过遵循一些规则获胜。

  • 一个识别器可以在任何时候自由地宣布失败并离开竞技场。当只剩下一个识别器时,它就赢了!
  • 一个识别器可以在任何时候宣布胜利,并迫使其他人宣称失败。

例如,假设你想识别垂直和水平拖曳。每个直接的识别器在收到第一个事件事件后就会进入手势竞技场。然后他们将以任何给定方向的逻辑像素来观察移动。

如果移动事件主要是垂直进行的,垂直识别器将由于逻辑像素的交叉而宣布胜利,因此该手势将被解释为在屏幕上的垂直拖动。同样的一系列事件也可以重复用于水平拖动。

如果你只想识别水平拖动,那么运动中是否有垂直成分并不重要,只有水平运动才会被识别。

在Flutter中编码触摸事件处理

setOnClickListener在Android中工作时,您可以通过OnClick ,来响应按钮的点击。但当涉及到Flutter时,您可以选择两种不同的方法来添加一个触摸监听器。

如果widget支持使用监听器,那么你可以传递一个回调函数来处理该事件。这里有一个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)
          ),
        );


  }
}

而这是它在你的应用程序中的样子。

onPressed action on a widget

GestureDetector示例

然而,你经常想使用一个小组件还不支持的监听器。当一个监听器不被小组件直接支持时,你可以将该小组件包装在GestureDetector ,并将处理程序传递给onTap 参数。下面是方法。

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();
  }
}

而这个应用程序会是这样的。

onTap action on a widget with GestureDetector

双击的动画示例

这里是另一个触摸事件反应的主要例子,双击Flutter标志会引起视觉旋转。

onDoubleTap action on a widget with GestureDetectoronDoubleTap action on a widget with GestureDetectoronDoubleTap action on a widget with GestureDetector

想知道如何监听双击事件吗?这就是方法。

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();
  }
}

最后的想法

在Flutter上对触摸事件做出反应并不像人们想象的那样复杂。了解小部件的性质和它们的监听能力是你需要的第一课。而其他的事情都会在这里顺利进行。