Skip to content

Gesture

GestureDetector 是手势识别组件,InkWell 是带水波纹效果的点击组件。

构造函数

GestureDetector

dart
GestureDetector({
  Key? key,                                                        // 组件标识
  Widget? child,                                                   // 子组件
  HitTestBehavior behavior = HitTestBehavior.deferToChild,       // 点击测试行为
  bool excludeFromSemantics = false,                              // 是否排除语义
  DragStartBehavior dragStartBehavior = DragStartBehavior.start, // 拖拽开始行为

  // 点击
  GestureTapCallback? onTap,                                      // 单击
  GestureTapCallback? onTapUp,                                    // 手指抬起
  GestureTapCallback? onTapDown,                                  // 手指按下
  GestureTapCancelCallback? onTapCancel,                          // 点击取消

  // 双击
  GestureTapCallback? onDoubleTap,                                // 双击

  // 长按
  GestureLongPressCallback? onLongPress,                          // 长按触发
  GestureLongPressStartCallback? onLongPressStart,                // 长按开始
  GestureLongPressMoveUpdateCallback? onLongPressMoveUpdate,     // 长按移动
  GestureLongPressEndCallback? onLongPressEnd,                   // 长按结束

  // 垂直拖拽
  GestureDragStartCallback? onVerticalDragStart,                  // 垂直拖拽开始
  GestureDragUpdateCallback? onVerticalDragUpdate,               // 垂直拖拽更新
  GestureDragEndCallback? onVerticalDragEnd,                     // 垂直拖拽结束

  // 水平拖拽
  GestureDragStartCallback? onHorizontalDragStart,               // 水平拖拽开始
  GestureDragUpdateCallback? onHorizontalDragUpdate,            // 水平拖拽更新
  GestureDragEndCallback? onHorizontalDragEnd,                  // 水平拖拽结束

  // 全方向拖拽(Pan)
  GestureDragStartCallback? onPanStart,                           // 全方向拖拽开始
  GestureDragUpdateCallback? onPanUpdate,                         // 全方向拖拽更新
  GestureDragEndCallback? onPanEnd,                              // 全方向拖拽结束

  // 缩放
  GestureScaleStartCallback? onScaleStart,                        // 缩放开始
  GestureScaleUpdateCallback? onScaleUpdate,                     // 缩放更新
  GestureScaleEndCallback? onScaleEnd,                           // 缩放结束

  // 力按压(3D Touch)
  GestureForcePressStartCallback? onForcePressStart,             // 力按压开始
  GestureForcePressPeakCallback? onForcePressPeak,               // 力按压峰值
  GestureForcePressEndCallback? onForcePressEnd,                 // 力按压结束

  // 速度
  GestureTapDownCallback? onSecondaryTap,                        // 辅助点击(右键)
  GestureTapDownCallback? onSecondaryTapDown,                    // 辅助点击按下
  GestureTapUpCallback? onSecondaryTapUp,                        // 辅助点击抬起
  GestureTapCancelCallback? onSecondaryTapCancel,               // 辅助点击取消
  GestureTapCallback? onTertiaryTapDown,                         // 第三按钮点击

  // 悬停(鼠标)
  PointerHoverEventListener? onHover,                             // 鼠标悬停
  PointerEnterEventListener? onEnter,                             // 鼠标进入
  PointerExitEventListener? onExit,                              // 鼠标离开
})

InkWell

dart
InkWell({
  Key? key,
  Widget? child,                                                   // 子组件
  GestureTapCallback? onTap,                                      // 单击
  GestureTapCallback? onDoubleTap,                                // 双击
  GestureLongPressCallback? onLongPress,                          // 长按
  GestureTapDownCallback? onTapDown,                              // 按下
  GestureTapUpCallback? onTapUp,                                  // 抬起
  GestureTapCancelCallback? onTapCancel,                          // 取消
  ValueChanged<bool>? onHover,                                    // 悬停状态变化
  ValueChanged<bool>? onFocusChange,                              // 焦点状态变化
  bool autofocus = false,
  FocusNode? focusNode,
  bool enableFeedback = true,                                     // 是否启用触觉反馈
  Color? focusColor,                                             // 焦点颜色
  Color? hoverColor,                                             // 悬停颜色
  Color? highlightColor,                                         // 高亮颜色
  Color? splashColor,                                            // 水波纹颜色
  MaterialInkController? splashFactory,                          // 水波纹工厂
  double? radius,                                                 // 水波纹半径
  ShapeBorder? customBorder,                                    // 自定义形状
  bool canRequestFocus = true,                                   // 是否可获取焦点
  MouseCursor? mouseCursor,                                      // 鼠标指针
  MaterialTapTargetSize? materialTapTargetSize,
})

InkResponse

dart
InkResponse({                                                      // 类似 InkWell,但水波纹从点击位置开始
  Key? key,
  Widget? child,
  GestureTapCallback? onTap,
  GestureTapCallback? onDoubleTap,
  GestureLongPressCallback? onLongPress,
  bool containedInkWell = false,                                  // 水波纹是否限制在边界内
  ShapeBorder? highlightShape,                                    // 高亮形状
  double? radius,
  Color? splashColor,
  Color? highlightColor,
  Color? focusColor,
  Color? hoverColor,
  bool enableFeedback = true,
  ...其余与 InkWell 类似
})

属性速查

GestureDetector 手势速查

手势类型回调说明
单击onTap点击触发
双击onDoubleTap快速双击
长按onLongPress长按 500ms 触发
水平拖拽onHorizontalDragStart/Update/End左右滑动
垂直拖拽onVerticalDragStart/Update/End上下滑动
全方向拖拽onPanStart/Update/End任意方向滑动
缩放onScaleStart/Update/End双指缩放/旋转
鼠标悬停onHover鼠标悬停(桌面端)
鼠标进入onEnter鼠标进入区域
鼠标离开onExit鼠标离开区域

HitTestBehavior 速查

说明
HitTestBehavior.deferToChild默认,子组件区域才响应(空白区域不响应)
HitTestBehavior.opaque整个区域响应,透明处理(阻止穿透)
HitTestBehavior.translucent整个区域响应,且允许穿透到下层

GestureDetector vs InkWell 对比

特性GestureDetectorInkWell
水波纹效果❌ 无✅ 有
触觉反馈❌ 无✅ 有(默认启用)
视觉反馈❌ 无✅ 有(高亮/悬停/焦点颜色)
手势范围非常全(拖拽/缩放/长按移动等)基础(点击/双击/长按)
无障碍需手动添加自带语义
适用场景复杂手势、自定义交互按钮/卡片/列表项点击

快速示例

基本点击

dart
// 无视觉效果(适合不可见点击区域)
GestureDetector(
  onTap: () => print('点击'),
  child: Text('点击我'),
)

// 有水波纹效果(推荐用于可见可点击元素)
InkWell(
  onTap: () => print('点击'),
  child: Padding(
    padding: EdgeInsets.all(12),
    child: Text('点击我'),
  ),
)

双击和长按

dart
GestureDetector(
  onDoubleTap: () => print('双击'),
  onLongPress: () => print('长按'),
  onTap: () => print('单击'),
  child: Container(
    padding: EdgeInsets.all(20),
    color: Colors.blue[100],
    child: Text('试试双击和长按'),
  ),
)

拖拽

dart
Offset offset = Offset.zero;

GestureDetector(
  onPanUpdate: (details) {
    setState(() {
      offset += details.delta;         // delta 是本次移动偏移量
    });
  },
  onPanEnd: (details) {
    print('最终速度: ${details.primaryVelocity}');
  },
  child: Transform.translate(
    offset: offset,
    child: Container(
      width: 80, height: 80,
      color: Colors.blue,
      child: Icon(Icons.drag_indicator, color: Colors.white),
    ),
  ),
)

缩放

dart
double scale = 1.0;
double previousScale = 1.0;

GestureDetector(
  onScaleStart: (details) {
    previousScale = scale;
  },
  onScaleUpdate: (details) {
    setState(() {
      scale = previousScale * details.scale;  // details.scale 是缩放倍数
    });
  },
  child: Transform.scale(
    scale: scale,
    child: Image.asset('assets/image.png'),
  ),
)

自定义 InkWell 样式

dart
InkWell(
  onTap: () {},
  splashColor: Colors.blue.withOpacity(0.3),     // 水波纹颜色
  hoverColor: Colors.blue.withOpacity(0.1),      // 悬停颜色
  highlightColor: Colors.blue.withOpacity(0.2),  // 高亮颜色
  borderRadius: BorderRadius.circular(8),        // 水波纹圆角(必须与容器匹配)
  child: Container(
    decoration: BoxDecoration(
      borderRadius: BorderRadius.circular(8),
      border: Border.all(color: Colors.blue),
    ),
    padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
    child: Text('自定义 InkWell'),
  ),
)

点击空白区域

dart
// ❌ 问题:GestureDetector 包裹空白区域不响应点击
GestureDetector(
  onTap: () {},
  child: Text('只有文字区域可点击'),
)

// ✅ 修复:设置 behavior 或用 Container 包裹
GestureDetector(
  behavior: HitTestBehavior.opaque,   // 整个区域都响应
  onTap: () {},
  child: SizedBox(
    width: 200,
    height: 50,
    child: Center(child: Text('整个区域可点击')),
  ),
)

滑动删除(Dismissible)

dart
Dismissible(
  key: Key(item.id),                    // 必填,唯一标识
  direction: DismissDirection.endToStart, // 仅从右往左滑
  background: Container(
    color: Colors.red,
    alignment: Alignment.centerRight,
    padding: EdgeInsets.only(right: 16),
    child: Icon(Icons.delete, color: Colors.white),
  ),
  onDismissed: (direction) {
    // 删除数据
    setState(() => items.remove(item));
  },
  child: ListTile(title: Text(item.name)),
)

常见错误

1. GestureDetector 拖拽与缩放冲突

dart
// ❌ 错误:onPan 和 onScale 不能同时使用(会冲突)
GestureDetector(
  onPanUpdate: ...,
  onScaleUpdate: ...,    // 不会触发!
)

// ✅ 修复:只用 onScale,它包含移动信息
GestureDetector(
  onScaleUpdate: (details) {
    // details.focalPoint 是焦点位置
    // details.scale 是缩放倍数
  },
)

2. InkWell 水波纹溢出圆角

dart
// ❌ 错误:InkWell 的 borderRadius 和 Container 不匹配
InkWell(
  borderRadius: BorderRadius.circular(8),
  child: Container(
    decoration: BoxDecoration(
      color: Colors.blue,         // ❌ 这会覆盖水波纹
      borderRadius: BorderRadius.circular(8),
    ),
    child: Text('按钮'),
  ),
)

// ✅ 修复:使用 Material + Ink 或用 Ink 替代 Container 的 color
Ink(
  decoration: BoxDecoration(
    color: Colors.blue,
    borderRadius: BorderRadius.circular(8),
  ),
  child: InkWell(
    borderRadius: BorderRadius.circular(8),
    onTap: () {},
    child: Padding(
      padding: EdgeInsets.all(12),
      child: Text('按钮', style: TextStyle(color: Colors.white)),
    ),
  ),
)

3. 手势竞争(点击 vs 拖拽)

dart
// ❌ 问题:同时设置了 onTap 和 onVerticalDrag,拖拽时也触发了 onTap
// ✅ 说明:Flutter 手势竞技场会自动处理,短距离移动仍为 onTap,长距离才为拖拽
// 如果需要更精细控制,使用 Listener 获取原始指针事件

基于 Flutter 官方文档整理