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 对比
| 特性 | GestureDetector | InkWell |
|---|---|---|
| 水波纹效果 | ❌ 无 | ✅ 有 |
| 触觉反馈 | ❌ 无 | ✅ 有(默认启用) |
| 视觉反馈 | ❌ 无 | ✅ 有(高亮/悬停/焦点颜色) |
| 手势范围 | 非常全(拖拽/缩放/长按移动等) | 基础(点击/双击/长按) |
| 无障碍 | 需手动添加 | 自带语义 |
| 适用场景 | 复杂手势、自定义交互 | 按钮/卡片/列表项点击 |
快速示例
基本点击
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 获取原始指针事件