Skip to content

Flutter - 手势简介

手势是用户与触控应用交互的主要方式。Flutter 提供了一个强大且灵活的系统,用于检测和响应各种用户手势,从简单的轻触到复杂的拖动和缩放。

移动应用用户期望应用能响应各种触控输入,包括:

  • 轻触 (Tap): 在屏幕表面短暂触碰。
  • 双击 (Double Tap): 在同一位置快速连续触碰两次。
  • 长按 (Long Press): 触碰屏幕并按住较长时间。
  • 拖动 / 平移 (Drag / Pan): 触碰屏幕并在其上移动手指。
    • 垂直拖动: 主要的垂直移动。
    • 水平拖动: 主要的水平移动。
  • 缩放 / 双指开合 (Scale / Pinch): 使用两根手指靠近(捏合/缩小)或分开(张开/放大)。
  • 滑动 / 抛掷 (Flick / Fling): 快速拖动并释放的动作,通常会导致持续的移动(例如滚动)。

Flutter 用于手势检测的主要工具是 GestureDetector 部件。它是一个非可视化的部件,这意味着它本身不绘制任何内容。相反,你将需要使其具备交互功能的部件包裹在 GestureDetector 内部。

GestureDetector 监听其子部件上的各种触控事件,并在识别到手势时调用相应的回调函数。

GestureDetector 为不同的手势提供了许多回调:

  • 轻触手势:

    • onTap:轻触手势完成时调用。
    • onTapDown:用户首次触碰屏幕,开始潜在轻触时调用。
    • onTapUp:用户释放屏幕,结束潜在轻触时调用。
    • onTapCancel:如果潜在轻触被取消(例如,手指移动太远)时调用。
    • onDoubleTap:双击时调用。
    • onLongPress:长按时调用。
  • 拖动手势(垂直、水平、平移):

    • onVerticalDragStart、onVerticalDragUpdate、onVerticalDragEnd
    • onHorizontalDragStart、onHorizontalDragUpdate、onHorizontalDragEnd
    • onPanStart、onPanUpdate、onPanEnd(适用于任意方向的拖动)
    • 这些回调提供了 DragUpdateDetails 或类似对象,包含关于拖动位置和增量 (delta) 的信息。
  • 缩放手势:

    • onScaleStart、onScaleUpdate、onScaleEnd
    • 提供 ScaleUpdateDetails,包含 scale、rotation、focalPoint 等信息。

GestureDetector 尝试为每次指针交互仅识别一个手势。当多个检测器可能从同一输入事件中识别出手势时(例如,决定是轻触还是垂直拖动),它会使用手势竞技场 (gesture arena) 来消除歧义。

示例:带对话框的可轻触的 ‘Hello World’

Section titled “示例:带对话框的可轻触的 ‘Hello World’”

让我们修改简单的 ‘Hello World’ 应用,使其在文本被轻触时显示一个对话框。

import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Gesture Demo',
home: const MyHomePage(title: 'Gesture Demo Home Page'),
);
}
}
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key, required this.title});
final String title;
// 显示对话框的函数
void _showDialog(BuildContext context) {
showDialog(
context: context,
builder: (BuildContext context) {
// 返回 Dialog 类型对象
return AlertDialog(
title: const Text("Message"),
content: const Text("Hello World Tapped!"),
actions: <Widget>[
TextButton( // 使用 TextButton 代替已弃用的 FlatButton
child: const Text("Close"),
onPressed: () {
Navigator.of(context).pop(); // 关闭对话框
},
),
],
);
},\n );
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(title)),
body: Center(
// 使用 GestureDetector 包裹 Text 部件
child: GestureDetector(
onTap: () {
// 轻触时调用显示对话框的函数
_showDialog(context);
},
child: const Text(
'Hello World',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
),
),
);
}
}

在此示例中,Text 部件被 GestureDetector 包裹。onTap 回调被实现为调用 _showDialog,该函数使用内置的 showDialog 函数和 AlertDialog 部件来显示一个简单的弹出框。

Flutter 提供了其他专门用于交互的部件:

  • Listener: 一个较低层级的部件,报告原始指针事件(PointerDownEvent、PointerMoveEvent、PointerUpEvent、PointerCancelEvent)。对于自定义手势识别或跟踪指针移动很有用。
  • InkWell 和 InkResponse: Material Design 部件,在轻触时显示视觉上的水波纹效果。常用于使非按钮部件可轻触并提供视觉反馈。
  • Dismissible: 允许通过滑动(抛掷)部件(通常是列表项)来将其移除。
  • Draggable 和 LongPressDraggable: 使部件可拖动。
  • DragTarget: 一个可以接受被拖放到其上的 Draggable 部件。
  • IgnorePointer 和 AbsorbPointer: 控制部件及其子树是否参与命中测试(手势检测)。IgnorePointer 对命中测试不可见,而 AbsorbPointer 会消耗命中事件但阻止下层部件接收它。
  • Scrollable 和可滚动视图部件(ListView、GridView、SingleChildScrollView): 自动处理滚动手势。

选择正确的部件取决于你想要实现的具体交互。GestureDetector 是通用工具,而 InkWell 或 Dismissible 等其他部件则提供更具体的行为和视觉效果。