Flutter 中的手势【Flutter 专题 10】

简介: 在 Flutter 中处理手势在创建应用程序时,您必须处理用户手势,例如触摸和拖动。这使您的应用程序具有交互性。为了有效地处理手势,您需要倾听手势并做出响应。Flutter 提供了各种小部件,有助于为您的应用程序添加交互性。在本文中,我们将使用GestureDetector小部件来处理手势。

在 Flutter 中处理手势

在创建应用程序时,您必须处理用户手势,例如触摸和拖动。这使您的应用程序具有交互性。


为了有效地处理手势,您需要倾听手势并做出响应。Flutter 提供了各种小部件,有助于为您的应用程序添加交互性。


在本文中,我们将使用GestureDetector小部件来处理手势。

介绍

一些小部件,如ContainerCard小部件,没有检测手势的内置方式。这些小部件被包裹在小部件中,GestureDetector小部件纯粹用于检测手势,不会像涟漪效应那样给出任何视觉响应。


GestureDetector小部件通过识别具有回调定义的手势并相应地响应事件来工作。如果要禁用手势,则会将null值传递给回调。


以下是GestureDetector小部件捕获的常见手势、它们对应的事件和可能的应用程序(所有插图均归功于Luke Wroblewski 的触摸手势参考指南):

轻敲

用户用指尖短暂地触摸了屏幕。


image.png


  • onTapDown — 当用户接触屏幕时触发,可能是点击
  • onTapUp — 当用户停止接触屏幕时触发
  • onTap — 当用户短暂触摸屏幕时触发
  • onTapCancel— 当触发的事件onTapDown不是点击时触发


点击手势的可能应用包括:


  1. 选择
  2. 取消
  3. 提交

双击

用户快速连续两次在同一位置点击屏幕。


image.png


  • onDoubleTapDown — 当用户接触屏幕时触发,可能是双击
  • onDoubleTap — 当用户快速连续两次点击同一位置的屏幕时触发
  • onDoubleTapCancel— 当触发的事件onDoubleTapDown不是双击时触发


双击手势的可能应用包括:


  1. 喜欢不喜欢
  2. 屏幕开/关
  3. 调整图像大小

长按

用户在同一位置长时间接触屏幕。


image.png


  • onLongPressDown — 当用户接触屏幕时触发,可能是长按
  • onLongPressStart — 检测到长按开始时触发
  • onLongPress — 检测到长按时触发
  • onLongPressMoveUpdate - 当检测到长按并且用户拖动手指时触发
  • onLongPressEnd — 检测到长按结束时触发
  • onLongPressUp— 检测到长按结束时触发;长按后联系人已删除
  • onLongPressCancel— 当触发的事件onLongPressDown不是长按时触发


长按手势的可能应用包括:


  1. 显示更多选项
  2. 移动图标

捏拉

用户捏住或展开屏幕。


image.png



  • onScaleStart — 当与屏幕接触已建立焦点且初始比例为 1.0 时触发
  • onScaleUpdate — 当与屏幕的接触指示新的焦点和/或比例时触发
  • onScaleEnd— 当用户不再接触screenPossible缩放手势的应用程序时触发


缩放手势的用途包括:


  1. 放大/缩小
  2. 回转

垂直拖动

用户接触屏幕并以稳定的方式垂直移动指尖。


image.png


  • onVerticalDragDown — 当用户接触屏幕时触发,可能会垂直移动
  • onVerticalDragStart — 当用户接触屏幕并开始垂直移动时触发
  • onVerticalDragUpdate — 当垂直移动的接触再次沿垂直方向移动时触发
  • onVerticalDragEnd — 在检测到垂直拖动结束时触发
  • onVerticalDragCancel— 当触发的事件onVerticalDragDown不是垂直拖动时触发


垂直拖动手势的可能应用包括:


  1. 滚动

水平拖动

用户接触屏幕并以稳定的方式水平移动指尖。


image.png


  • onHorizontalDragDown — 当用户接触屏幕时触发,可能会水平移动
  • onHorizontalDragStart — 当用户接触屏幕并开始水平移动时触发
  • onHorizontalDragUpdate — 当水平移动的接触再次在水平方向移动时触发
  • onHorizontalDragEnd — 当检测到水平拖动结束时触发
  • onHorizontalDragCancel— 当触发的事件onHorizontalDragDown不是水平拖动时触发


水平拖动手势的可能应用包括:


  1. 删除
  2. Archive
  3. 导航到不同的页面


这不是检测到的手势的完整列表。查看官方文档以获取完整列表。


好的,上面介绍了手势相关的使用场景等,接下来


让我们试试吧!

入门

要使用GestureDetector小部件:


  1. GestureDetector小部件包装所需的小部件。
  2. 为您希望检测的手势传递回调。
  3. 相应地更新应用程序


我们将构建一个简单的演示应用程序来处理单击、双击、长按和缩放手势。

创建一个新的 Flutter 应用

创建一个新的 Flutter 应用程序并清除文件中的默认代码。main.dart

更新用户界面

我们将创建下面的四个文件。您可以在此处查看文件夹结构

main.dart

import 'package:flutter/material.dart';
import 'presentation/my_app_widget.dart';
void main() {
  runApp(const MyApp());
}


my_app_widget.dart

import 'package:flutter/material.dart';
import 'home_page.dart';
class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Gesture Detector Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home:  const HomePage(),
    );
  }
}

home_page.dart

import 'package:flutter/material.dart';
import 'widgets/widgets.dart';
class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    final height = MediaQuery.of(context).size.height;
    final width = MediaQuery.of(context).size.width;
    return Scaffold(
      body: Padding(
        padding: EdgeInsets.symmetric(
            horizontal: width * 0.1, vertical: height * 0.2),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children:  const [
            MyCardWidget(),
            MyFavoriteIconWidget()
          ],
        ),
      ),
    );
  }
}


my_card_widget.dart

import 'dart:math';
import 'package:flutter/material.dart';
class MyCardWidget extends StatefulWidget {
  const MyCardWidget({
    Key? key,
  }) : super(key: key);
  @override
  State<MyCardWidget> createState() => _MyCardWidgetState();
}
class _MyCardWidgetState extends State<MyCardWidget> {
  @override
  Widget build(BuildContext context) {
    return const Card(
      child: SizedBox(
        height: 300,
        width: 300,
      ),
      color: Colors.yellow,
    );
  }
}


my_favorite_icon_widget.dart

import 'package:flutter/material.dart';
class MyFavoriteIconWidget extends StatefulWidget {
  const MyFavoriteIconWidget({
    Key? key,
  }) : super(key: key);
  @override
  State<MyFavoriteIconWidget> createState() => _MyFavoriteIconWidgetState();
}
class _MyFavoriteIconWidgetState extends State<MyFavoriteIconWidget> {
  @override
  Widget build(BuildContext context) {
    return const Icon(
     Icons.favorite_border,
      size: 40,
    );
  }
}


您的最终应用程序应如下所示:

image.png


现在我们已经准备好了 UI,让我们处理一些手势。

处理点击手势

在您的文件中:my_favorite_icon_widget.dart


  1. 将选定的标志属性添加到 StatefulWidgetbool isSelected = false;
  2. IconGestureDetector小部件包裹小部件
  3. onTap属性提供非空回调
  4. 根据 flag 属性值改变图标和图标颜色
class _MyFavoriteIconWidgetState extends State<MyFavoriteIconWidget> {
  bool isSelected = false;
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
        onTap: (){
          setState(() {
            isSelected = !isSelected;
          });
        },
        child:  Icon(
          isSelected ? Icons.favorite: Icons.favorite_border,
          size: 40,
          color: isSelected? Colors.red: Colors.black ,
        ));
  }
}

处理双击手势

在您的文件中:my_card_widget.dart


  1. 添加颜色属性
  2. Card小部件包装小GestureDetector部件
  3. onDoubleTap属性提供非空回调
  4. 根据颜色属性的值改变卡片的颜色
class _MyCardWidgetState extends State<MyCardWidget> {
  Color bgColor = Colors.yellow;
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onDoubleTap: (){
        setState(() {
          bgColor = Colors.primaries[Random().nextInt(Colors.primaries.length)];
        });
      },
      child:   Card(
        child: const SizedBox(
          height: 300,
          width: 300,
        ),
        color: bgColor,
      ),
    );
  }
}

处理长按手势

在您的文件: 1.添加标记属性 2.提供一个非空回调的财产 基础上的价值 3.更改卡的形状属性my_card_widget.dartmakeCircularonLongPressmakeCircular

class _MyCardWidgetState extends State<MyCardWidget> {
  Color bgColor = Colors.yellow;
  bool makeCircular = false;
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onLongPress: (){
        setState(() {
          makeCircular = !makeCircular;
        });
      },
      child:   Card(
        shape: makeCircular? const CircleBorder(): const RoundedRectangleBorder(),
        child: const SizedBox(
          height: 300,
          width: 300,
        ),
        color: bgColor,
      ),
    );
  }
}

处理缩放手势

在您的文件中: 1. 添加一个属性 2. 添加一个属性 3. 为该属性提供一个非空回调——建立一个初始比例 4.为该属性提供一个非空回调——建立一个新的比例 5. 提供一个属性的非空回调— 返回初始比例 6.用小部件包裹小部件 7. 根据my_card_widget.dart_scaleFactor_baseFactoronScaleStartonScaleUpdateonScaleEndCard``Transorm.scale_scaleFactor

class _MyCardWidgetState extends State<MyCardWidget> {
  Color bgColor = Colors.yellow;
  bool makeCircular = false;
  double _scaleFactor = 0.5;
  double _baseScaleFactor = 0.5;
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onScaleStart: (details){
        _baseScaleFactor = _scaleFactor;
      },
      onScaleUpdate: (details){
        setState(() {
          _scaleFactor = _baseScaleFactor * details.scale;
        });
      },
      onScaleEnd: (details){
        // return to initial scale
        _scaleFactor = _baseScaleFactor;
      },
      child:   Transform.scale(
        scale: _scaleFactor,
        child: Card(
          shape: makeCircular? const CircleBorder(): const RoundedRectangleBorde(),
          child: const SizedBox(
            height: 300,
            width: 300,
          ),
        color: bgColor,
      ),
    );
  }
}

<img src="https://luckly007.oss-cn-beijing.aliyuncs.com/images/1.jpg" alt="1" style="zoom:25%;" />


<img src="https://luckly007.oss-cn-beijing.aliyuncs.com/images/2.jpg" alt="2" style="zoom:25%;" />

手势消歧

那么当我们onGestureDown为单击和双击提供 事件回调,并且发生两个延迟的、短暂的触摸事件时会发生什么?


考虑下图:


image.png


当识别出两个或多个具有非空回调的手势事件时,Flutter 通过让每个识别器加入手势领域来消除用户想要的手势的歧义。在手势竞技场中, “战斗” 事件和获胜事件生效,而失败事件被取消。


手势领域考虑了以下因素:


  1. 用户触摸屏幕的时间长度
  2. 每个方向移动的像素数
  3. 哪个手势在竞技场
  4. 哪个手势宣告胜利


这些是竞争状态:


  • 也许——也许是手势
  • 保持——如果它以特定的方式发展,可能是一种姿态;对于我们的案例,如果第二次点击发生在预期时间内,则发生了一次点击并且可能是两次点击
  • 是——获得优先
  • 取消——退出竞争


例如,假设发生以下情况:


1.onTapDownonDoubleTapDown被触发


2 两个手势竞争 3.单击手势获胜并执行onTap回调(回调)


4.单击手势失败并取消(onDoubleTapCancel触发)


对于我们的案例,点击手势竞争是因为:


  • 两次敲击之间的持续时间被延迟
  • 点击手势以“是”宣布胜利
  • 点击手势是取消双击后剩下的手势,没有其他竞争对手

结论

我们已经浏览了GestureDetector小部件并了解了它的工作原理。我们已经学会了如何使用它为我们的应用程序添加交互性,并且我们已经实现了一些常见的手势,比如点击、长按、双击和缩放。我们最后还研究了手势消歧。


有了这些知识,我们现在对GestureDetector小部件有了更好的理解,并且可以轻松地使用它的任何属性来识别手势。随意玩弄不同的手势。您可以在 GitHub 上找到演示应用程序

相关文章
flutter系列之:移动端手势的具体使用
之前我们介绍了GestureDetector的定义和其提供的一些基本的方法,GestureDetector的好处就是可以把任何一个widget都赋予类似button的功能。 今天将会通过几个具体的例子来讲解一下GestureDetector的具体使用。
flutter系列之:移动端手势的具体使用
【Flutter框架】Flutter项目对于手势的运用以及单独指针、消歧问题的解决方案
【Flutter框架】Flutter项目对于手势的运用以及单独指针、消歧问题的解决方案
|
开发框架 移动开发 iOS开发
flutter系列之:移动端的手势基础GestureDetector
移动的和PC端有什么不同呢?同样的H5可以运行在APP端,也可以运行在PC端。两者最大的区别就是移动端可以用手势。手势可以做到一些比如左滑右滑,上滑下滑,缩放等操作。 原生的andorid和IOS当然可以做到这些事情,作为一个移动的的开发框架flutter,自然也能够支持手势。flutter中的手势支持叫做GestureDetector,一起来看看flutter中的手势基础吧。
|
Dart 开发者
【Flutter】Flutter 手势交互 ( 点击事件处理 | 点击 onTap | 双击 | 长按 onLongPress | 点击取消 | 按下 onTapDown | 抬起 onTapUp )(二)
【Flutter】Flutter 手势交互 ( 点击事件处理 | 点击 onTap | 双击 | 长按 onLongPress | 点击取消 | 按下 onTapDown | 抬起 onTapUp )(二)
433 0
【Flutter】Flutter 手势交互 ( 点击事件处理 | 点击 onTap | 双击 | 长按 onLongPress | 点击取消 | 按下 onTapDown | 抬起 onTapUp )(二)
|
Dart 开发者
Flutter 手势原理
Flutter 手势原理
251 0
Flutter 手势原理
【Flutter】Flutter 手势交互 ( 点击事件处理 | 点击 onTap | 双击 | 长按 onLongPress | 点击取消 | 按下 onTapDown | 抬起 onTapUp )(一)
【Flutter】Flutter 手势交互 ( 点击事件处理 | 点击 onTap | 双击 | 长按 onLongPress | 点击取消 | 按下 onTapDown | 抬起 onTapUp )(一)
483 0
|
3月前
|
开发框架 前端开发 测试技术
Flutter开发常见问题解答
Flutter开发常见问题解答
|
15天前
|
JSON Dart Java
flutter开发多端平台应用的探索
flutter开发多端平台应用的探索
26 6
|
15天前
|
JSON Dart Java
flutter开发多端平台应用的探索 下 (跨模块、跨语言通信之平台通道)
flutter开发多端平台应用的探索 下 (跨模块、跨语言通信之平台通道)