事件与通知
Flutter中的触摸事件可分为两层来看待。第一层是触摸原事件(指针),有相应的四种事件类型:PointerDownEvent:用户与屏幕接触产生了联系。PointerMoveEvent:手指已从屏幕上的一个位置移动到另一个位置。PointerUpEvent:用户已停止接触屏幕。PointerCancelEvent:此指针的输入不再指向此应用程序。第二层就是检测到的手势,主要分为三大类,包括轻击、拖动和缩放
GestureDetector
GestureDetector 可以进行手势检测,共有7种类型事件。比如点击一次、双击、长按、垂直滑动及水平滑动等

| 属性 | 类型 | 简述 |
|---|---|---|
| onTapDown | GestureTapDownCallback |
手指按下时触发 |
| onTapUp | GestureTapUpCallback |
手指抬起时触发 |
| onTap | GestureTapCallback |
单击屏幕时触发 |
| onTapCancel | GestureTapCancelCallback |
没有完成Tap的动作时触发 |
| onDoubleTap | GestureTapCallback |
快速双击屏幕时触发 |
| onLongPress | GestureLongPressCallback |
长按屏幕时触发 |
| onLongPressStart | GestureLongPressStartCallback |
监听长按事件的开始 |
| onLongPressMoveUpdate | GestureLongPressMoveUpdateCallback |
长按屏幕且移动手指时触发 |
| onLongPressUp | GestureLongPressUpCallback |
手指完全离开屏幕时触发 |
| onLongPressEnd | GestureLongPressEndCallback |
手指开始离开屏幕时触发 |
| onVerticalDragDown | GestureDragDownCallback |
手指按下并在垂直方向上移动时触发 |
| onVerticalDragStart | GestureDragStartCallback |
触摸点开始在垂直方向上移动时触发(在onVerticalDragDown之后) |
| onVerticalDragUpdate | GestureDragUpdateCallback |
触摸点在垂直方向上移动时触发 |
| onVerticalDragEnd | GestureDragEndCallback |
停止移动,且该拖拽操作就被认为是完成了时触发 |
| onVerticalDragCancel | GestureDragCancelCallback |
突然停止拖拽时触发 |
| onHorizontalDragDown | GestureDragDownCallback |
参见onVerticalDragDown,为水平方向 |
| onHorizontalDragStart | GestureDragStartCallback |
参见onVerticalDragDown,为水平方向 |
| onHorizontalDragUpdate | GestureDragUpdateCallback |
参见onVerticalDragDown,为水平方向 |
| onHorizontalDragEnd | GestureDragEndCallback |
参见onVerticalDragDown,为水平方向 |
| onHorizontalDragCancel | GestureDragCancelCallback |
参见onVerticalDragDown,为水平方向 |
| onPanDown | GestureDragDownCallback |
手指接触屏幕,并且可能开始移动时触发 |
| onPanStart | GestureDragStartCallback |
触摸点开始移动时触发 |
| onPanUpdate | GestureDragUpdateCallback |
触摸点不断移动时触发 |
| onPanEnd | GestureDragEndCallback |
操作完成手指离开屏幕时触发 |
| onPanCancel | GestureDragCancelCallback |
先前触发onPanDown的指针未完成时触发 |
| onScaleStart | GestureScaleStartCallback |
触摸屏幕开始缩放操作时触发 |
| onScaleUpdate | GestureScaleUpdateCallback |
缩放过程中的监听 |
| onScaleEnd | GestureScaleEndCallback |
手指离开屏幕,缩放完成时触发 |
| onForcePressStart | GestureForcePressStartCallback |
触摸屏幕且有足够压力时触发(仅在具有压力检测的屏幕设备支持) |
| onForcePressPeak | GestureForcePressPeakCallback |
触摸屏幕压力达到最大时触发(需设备支持) |
| onForcePressUpdate | GestureForcePressUpdateCallback |
有足够的压力并在屏幕上移动时触发(需设备支持) |
| onForcePressEnd | GestureForcePressEndCallback |
离开屏幕时触发(需设备支持) |
| behavior | HitTestBehavior |
在命中测试期间,此手势检测器应如何表现 |
| excludeFromSemantics | bool |
是否从语义树中排除这些手势。 例如,用于显示工具提示的长按手势被排除在外,因为工具提示本身直接包含在语义树中,因此具有显示该工具提示的手势将导致信息重复 |
| dragStartBehavior | DragStartBehavior |
设定处理拖动开始行为的方式 |
需要注意,GestureDetector并不会监听所有的手势,只有传入的回调非空时,才会监听。所以,如果想要禁用某个手势时,可将对应的回调函数设置为null。
另外,GestureDetector的某些事件是互斥的,不能同时存在,例如onVerticalUpdate、onHorizontalUpdate、onPanUpdate这三个事件不能同时存在,onPanUpdate和onScaleUpdate也不能同时存在。
测试手势处理回调
GestureDetector(
child: Container(
width: 300.0,
height: 500.0,
color: Colors.amber,
),
onTapDown: (_) => debugPrint("onTapDown"),
onTapUp: (_) => debugPrint("onTapUp"),
onTap: () => debugPrint("onTap"),
onTapCancel: () => debugPrint("onTapCancel"),
onDoubleTap: () => debugPrint("onDoubleTap"),
onLongPress: () => debugPrint("onLongPress"),
onLongPressUp: () => debugPrint("onLongPressUp"),
onVerticalDragDown: (_) => debugPrint("onVerticalDragDown"),
onVerticalDragStart: (_) => debugPrint("onVerticalDragStart"),
onVerticalDragUpdate: (_) => debugPrint("onVerticalDragUpdate"),
onVerticalDragEnd: (_) => debugPrint("onVerticalDragEnd"),
onVerticalDragCancel: () => debugPrint("onVerticalDragCancel"),
onHorizontalDragDown: (_) => debugPrint("onHorizontalDragDown"),
onHorizontalDragStart: (_) => debugPrint("onHorizontalDragStart"),
onHorizontalDragUpdate: (_) => debugPrint("onHorizontalDragUpdate"),
onHorizontalDragEnd: (_) => debugPrint("onHorizontalDragEnd"),
onHorizontalDragCancel: () => debugPrint("onHorizontalDragCancel"),
onPanDown: (_) => debugPrint("onPanDown"),
onPanStart: (_) => debugPrint("onPanStart"),
onPanUpdate: (_) => debugPrint("onPanUpdate"),
onPanEnd: (_) => debugPrint("onPanEnd"),
onPanCancel: () => debugPrint("onPanCancel"),
onScaleStart: (_) => debugPrint("onScaleStart"),
onScaleUpdate: (_) => debugPrint("onScaleUpdate"),
onScaleEnd: (_) => debugPrint("onScaleEnd"),
),
通过手势处理实现一个拖动示例
class _HomePageState extends State<HomePage> {
double _left = 0.0;
double _top = 0.0;
GlobalKey _gKey = GlobalKey();
double stackWidth = 0.0;
double stackHeight = 0.0;
@override
void initState() {
super.initState();
// 注册一个回调,当屏幕渲染第一帧的时候回调
WidgetsBinding.instance.addPostFrameCallback((_){
stackWidth = _gKey.currentContext.size.width;
stackHeight = _gKey.currentContext.size.height;
});
}
@override
Widget build(BuildContext context) {
print("_HomePageState build ...");
return Scaffold(
appBar: AppBar(
title: Text("Flutter Widget"),
),
body: GestureDetector(
onPanUpdate: (DragUpdateDetails details){
setState(() {
// 边界检查
if(_left + 200 > stackWidth){
_left = stackWidth- 200;
}else if(_left < 0){
_left = 0;
} else{
_left += details.delta.dx;
}
if(_top + 200 > stackHeight){
_top = stackHeight- 200;
}else if(_top < 0){
_top = 0;
}else{
_top += details.delta.dy;
}
});
},
child: Stack(
key: _gKey,
children: <Widget>[
Positioned(
left: _left,
top: _top,
width: 200.0,
height: 200.0,
child: Container(
color: Colors.amber,
),
)
],
),
),
);
}
}
缩放示例
class _ScaleWidgetState extends State<ScaleWidget>{
double _width = 300.0;
double _height = 200.0;
@override
Widget build(BuildContext context) {
return Center(
child: GestureDetector(
child: Container(
width: _width,
height: _height,
color: Colors.pink,
),
onScaleUpdate: (e) {
print(e);
setState(() {
// 限制缩放比例在0.7-1.2之间,超过范围则变为原大小
_width = 300* e.scale.clamp(0.7, 1.2);
_height = 200*e.scale.clamp(0.7, 1.2);
});
},
)
);
}
}
InkWell
具有水波纹效果(或称为溅墨效果)的点击事件控件。能处理的触摸事件很少,如需处理复杂的手势事件,应使用GestureDetector。
需要注意,当InkWell的父控件设置了背景色时,是看不到溅墨效果效果的,此时需要进行特殊处理,当使用InkWell包裹image时,也无法显示出溅墨效果,这时建议使用Ink.Image控件。
// 需使用Matetial 以及 Ink控件包裹
Material(
child: Ink(
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(10)
),
child: InkWell(
onTap: (){
print("onTap...");
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 16,vertical: 6),
child: Text("这是按钮",style: TextStyle(color: Colors.white),),
),
),
),
)
如需取消溅墨效果,可进行如下设置
// 设置highlightColor和radius取消溅墨效果
InkWell(
highlightColor: Colors.transparent,
radius: 0.0,
child: Text(
"click me",
),
onTap: () {
print("onTap");
},
),
Snackbar
它是Material Design设计中的一个轻量级消息通知控件。
| 属性 | 类型 | 简述 |
|---|---|---|
| content | Widget |
展示的内容 |
| backgroundColor | Color |
背景色 |
| elevation | double |
阴影高度 |
| shape | ShapeBorder |
形状 |
| behavior | SnackBarBehavior |
位置,SnackBarBehavior.fixed 固定在底部;SnackBarBehavior.floating显示在底部导航栏上方 |
| action | SnackBarAction |
执行的动作(相当于按钮) |
| duration | Duration |
停留时间 |
| animation | animation |
显示或隐藏的动画效果 |
| onVisible | VoidCallback |
显示时的回调 |
Scaffold.of(context).showSnackBar(SnackBar(
content: Text('这是一个 SnackBar'),
backgroundColor: Colors.black26,
duration: Duration(seconds: 1),
behavior: SnackBarBehavior.floating,
action: SnackBarAction(
label: '我是按钮',
onPressed: () {
print('点击了按钮');
}),
));
小结:
显示
Snackbar:Scaffold.of(context).showSnackBar(snackBar)隐藏当前的
SnackBar:Scaffold.of(context).hideCurrentSnackBar()
公众号“编程之路从0到1”