原始指针事件
概念
指针事件是Flutter中最基础的事件类型,直接对应触摸屏、鼠标、手写笔等输入设备的原始交互。一个完整的原始指针事件主要由手指按下、手指移动、手指抬起,以及触摸取消构成,更高级别的手势都基于这些原始事件。
PointerEvent是Flutter原始指针事件的基础类
Dart
position:全局坐标的偏移量
delta:两次指针移动事件的距离
pressure:按压力度,如果手机屏幕支持压力传感器,此属性会返回压力值,如果手机不支持,则始终返回1。
orientation:指针移动方向,是一个角度值
核心指针事件类型
Dart
- PointerDownEvent : 手指/指针按下
- PointerMoveEvent : 手指/指针移动
- PointerUpEvent : 手指/指针抬起
- PointerCancelEvent: 事件取消(如被手势识别器拦截)
- PointerSignalEvent: 滚轮等信号
指针事件监听器的一个小案例
Dart
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Listener(
onPointerDown: (PointerDownEvent event) {
print('按下 - 位置: ${event.position}');
print('本地位置: ${event.localPosition}');
print('指针ID: ${event.pointer}');
print('压力: ${event.pressure}');
print('设备类型: ${event.kind}'); // touch, mouse, stylus等
},
onPointerMove: (PointerMoveEvent event) {
print('移动 - 当前位置: ${event.position}');
print('移动距离: ${event.delta}');
print('globalPosition: ${event.globalPosition}'); // 相对于整个屏幕
print('localPosition: ${event.localPosition}'); //相对于当前 Listener 的坐标
},
onPointerUp: (PointerUpEvent event) {
print('抬起');
},
onPointerCancel: (PointerCancelEvent event) {
print('事件取消');
},
onPointerSignal: (PointerSignalEvent event) {
if (event is PointerScrollEvent) {
print('滚轮滚动: ${event.scrollDelta}');
}
},
behavior: HitTestBehavior.opaque, // 事件穿透行为
child: Container(
width: 200,
height: 200,
color: Colors.blue,
),
),
)
);
}
PointerEvent 对象的详细属性
Dart
Listener(
onPointerDown: (event) {
// 基本信息
print('位置: ${event.position}'); // 全局坐标
print('本地位置: ${event.localPosition}'); // 相对于当前widget的坐标
print('指针ID: ${event.pointer}'); // 用于追踪同一触摸点
print('时间戳: ${event.timeStamp}'); // 事件发生时间
// 触摸信息
print('压力: ${event.pressure}'); // 触摸压力(0-1)
print('压力最大值: ${event.pressureMax}');
print('接触面积: ${event.size}'); // 触摸面积
// 设备信息
print('设备类型: ${event.kind}'); // PointerDeviceKind.touch/mouse/stylus等
print('设备是否支持压力: ${event.synthesized}');
// 辅助信息
print('按钮: ${event.buttons}'); // 鼠标按钮
print('倾斜: ${event.tilt}'); // 手写笔倾斜角度
print('方位角: ${event.orientation}'); // 手写笔方向
},
child: Container(...),
)
behavior属性:决定子组件如何响应命中测试,它的值的类型为HitTestBehavior,是一个枚举类型,有3个枚举值
Dart
enum HitTestBehavior {
/// 透明区域不响应,只响应不透明区域
deferToChild,
/// 透明区域也响应,但不阻止事件传递
translucent,
/// 总是响应,并阻止事件传递
opaque,
}
手势识别组件(Gesture API )
常用事件的小案例
Dart
return Scaffold(
body: Center(
child: GestureDetector(
// 点击相关
onTap: () => print('点击'),
onTapDown: (details) => print('按下: ${details.globalPosition}'),
onTapUp: (details) => print('抬起'),
onTapCancel: () => print('点击取消'),
// 双击
onDoubleTap: () => print('双击'),
onDoubleTapDown: (details) => print('双击按下'),
onDoubleTapCancel: () => print('双击取消'),
// 长按
onLongPress: () => print('长按'),
onLongPressStart: (details) => print('长按开始'),
onLongPressEnd: (details) => print('长按结束'),
onLongPressMoveUpdate: (details) => print('长按移动'),
onLongPressUp: () => print('长按抬起'),
// 水平拖动
onHorizontalDragStart: (details) => print('水平拖动开始'),
onHorizontalDragUpdate: (details) => print('水平拖动中'),
onHorizontalDragEnd: (details) => print('水平拖动结束'),
// 垂直拖动
onVerticalDragStart: (details) => print('垂直拖动开始'),
onVerticalDragUpdate: (details) => print('垂直拖动中'),
onVerticalDragEnd: (details) => print('垂直拖动结束'),
// 其他
behavior: HitTestBehavior.opaque, // 命中测试行为
excludeFromSemantics: false, // 是否排除语义
child: Container(
width: 200,
height: 200,
color: Colors.blue,
child: Center(child: Text('点击我')),
),
)
)
);
缩放的小案例
Dart
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<StatefulWidget> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
// 当前缩放和位置
double _scale = 1.0; //当前缩放比例
Offset _position = Offset.zero; //当前位置
// 用于平滑缩放的中间变量
double _previousScale = 1.0; //上一次缩放开始前的比例
Offset _previousPosition = Offset.zero;//上一次缩放开始前的位置
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('图片手势'),
actions: [
IconButton(
icon: const Icon(Icons.restore),
onPressed: _resetImage,
tooltip: '恢复默认大小和位置',
),
],
),
body: Column(
children: [
Expanded(
child: GestureDetector(
// 缩放开始
onScaleStart: (details) {
// 保存开始缩放前的状态
_previousScale = _scale;
_previousPosition = _position;
},
// 缩放更新
onScaleUpdate: (details) {
setState(() {
double scaleChange = details.scale; //表示相对于上一次的缩放比例变化
_scale = _previousScale * scaleChange;//计算新的缩放值
// 限制缩放范围
_scale = _scale.clamp(0.5, 3.0);
// 更新位置
_position = _previousPosition + details.focalPointDelta;
});
},
// 缩放结束
onScaleEnd: (details) {
// 可以在这里添加回弹效果等
},
// 双击重置
onDoubleTap: _resetImage,
child: Center(
child: Transform.scale(
scale: _scale,
child: Transform.translate(
offset: _position,
child: Image.asset(
'assets/images/apple.png',
// 添加错误处理
errorBuilder: (context, error, stackTrace) {
return Container(
width: 200,
height: 200,
color: Colors.grey[300],
child: const Center(
child: Text('图片加载失败'),
),
);
},
),
),
),
),
),
),
],
),
);
}
// 重置图片到原始大小和位置
void _resetImage() {
setState(() {
_scale = 1.0;
_position = Offset.zero;
});
}
}
拖拽的小案例
Dart
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<StatefulWidget> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
double _top = 0.0;
double _left = 0.0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('图片拖拽'),
actions: [
IconButton(
icon: const Icon(Icons.restore),
onPressed: _resetImage,
tooltip: '恢复默认大小和位置',
),
],
),
body: Stack(
children: [
Positioned(
top: _top,
left: _left,
child: GestureDetector(
child: Image.asset("assets/images/apple.png"),
onPanDown: (DragDownDetails e){
print("onPanDown:${e.globalPosition}");
},
onPanUpdate: (DragUpdateDetails e){
setState(() {
_left += e.delta.dx;
_top += e.delta.dy;
});
},
onPanEnd: (DragEndDetails e){
print('onPanEnd:'+e.velocity.toString());
},
),
)
],
),
);
}
// 重置图片到原始大小和位置
void _resetImage() {
setState(() {
_top = 0.0;
_left = 0.0;
});
}
}
InkWell - 带有水波纹效果的点击组件
Dart
@override
Widget build(BuildContext context) {
return Material(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 基础用法
InkWell(
onTap: () => print('InkWell 点击'),
onDoubleTap: () => print('InkWell 双击'),
onLongPress: () => print('InkWell 长按'),
splashColor: Colors.blue.withOpacity(0.5), // 水波纹颜色
highlightColor: Colors.red.withOpacity(0.2), // 高亮颜色
radius: 100, // 水波纹半径
borderRadius: BorderRadius.circular(10), // 圆角
child: Container(
width: 200,
height: 100,
child: Center(child: Text('带水波纹的点击区域')),
),
),
SizedBox(height: 20),
// 带边框的 InkWell
Material(
borderRadius: BorderRadius.circular(15),
child: InkWell(
onTap: () => print('带边框的点击'),
borderRadius: BorderRadius.circular(15),
child: Container(
width: 200,
height: 100,
child: Center(child: Text('带边框的水波纹')),
),
),
),
],
),
),
);
}
待学习
自定义组件
手势密码锁