Listener介绍
Listener
它是主要的功能是用来监听屏幕触摸事件,取决于它的子组件区域范围,比如按下、移动、抬起、取消等操作时可以添加监听。
我们知道Flutter
组件只有按钮才会有事件,那么如果我需要在文字或者某个容器上添加事件那我就需要借助Listener
视频教程地址
手势系列视频教程地址
在什么情况下使用Listener?
Listener
常用于当手指滑动屏幕时进行隐藏键盘或者下拉刷新、上拉加载时进行事件监听。
一般在实际的开发过程中我们很少会用到Listener
来监听手势,一般都是通过GestureDetector
来进行监听或者使用MouseRegion
来监听鼠标的事件,而MouseRegion
常用于web开发中,GestureDetector
常用于app。
Listener原理
- 当指针按下时,Flutter会对应用程序执行命中测试(Hit Test),以确定指针与屏幕接触的位置存在哪些组件(widget)
- 指针按下事件(以及该指针的后续事件)然后被分发到由命中测试发现的最内部的组件
- 事件会沿着最内部的组件向组件树的根冒泡分发
- 没有机制取消或停止“冒泡”过程
Listener构造函数
我们经常使用的回调函数主要有三个
- onPointerDown()
- onPointerMove()
- onpointUp()
const Listener({
Key key,
this.onPointerDown,
this.onPointerMove,
// We have to ignore the lint rule here in order to use deprecated
// parameters and keep backward compatibility.
// TODO(tongmu): After it goes stable, remove these 3 parameters from Listener
// and Listener should no longer need an intermediate class _PointerListener.
// https://github.com/flutter/flutter/issues/36085
@Deprecated(
'Use MouseRegion.onEnter instead. See MouseRegion.opaque for behavioral difference. '
'This feature was deprecated after v1.10.14.'
)
this.onPointerEnter,
@Deprecated(
'Use MouseRegion.onExit instead. See MouseRegion.opaque for behavioral difference. '
'This feature was deprecated after v1.10.14.'
)
this.onPointerExit,
@Deprecated(
'Use MouseRegion.onHover instead. See MouseRegion.opaque for behavioral difference. '
'This feature was deprecated after v1.10.14.'
)
this.onPointerHover,
this.onPointerUp,
this.onPointerCancel,
this.onPointerSignal,
this.behavior = HitTestBehavior.deferToChild,
Widget child,
}) : assert(behavior != null),
_child = child,
super(key: key);
Listener属性和说明
字段 | 属性 | 描述 |
---|---|---|
onPointerDown | PointerDownEventListener | 指针按下时触发回调 |
onPointerMove | PointerMoveEventListener | 指针移动时触发回调 |
onPointerUp | PointerUpEventListener | 指针移开时触发回调 |
onPointerSignal | PointerSignalEventListener | 当指针信号出现时调用 |
onPointerCancel | PointerCancelEventListener | 指针取消时触发回调 |
onPointerEnter | PointerEnterEventListener | 当指针进入区域时回调(已废弃) |
onPointerExit | PointerExitEventListener | 当指针移出区域时回调(已废弃) |
onPointerHover | PointerHoverEventListener | 当没有触发 [onPointerDown] 的指针改变时调用 |
behavior | HitTestBehavior | 在命中测试期间如何表现 |
child | Widget | 子组件 |
Listener基本使用
我们这里主要是针对onPointerDown
、onPointerMove
、onPointerUp
进行演示,因为我们在平时的开发过程中最常用到的属性就是这三个,而且其他的属性也都被废弃掉了。
import 'package:flutter/material.dart';
class ListenerExample extends StatefulWidget {
@override
_ListenerExampleState createState() => _ListenerExampleState();
}
class _ListenerExampleState extends State {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Listener"),
),
body: Center(
child: Stack(
children: [
Listener(
onPointerDown: (event) {
print("onPointerDown----$event");
},
onPointerMove: (event) {
print("onPointerMove----$event");
},
onPointerUp: (event) {
print("onPointerUp----$event");
},
// onPointerSignal: (event) {
// print("onPointerSignal----$event");
// },
// onPointerCancel: (event) {
// print("onPointerCancel----$event");
// },
// onPointerEnter: (event) {
// print("onPointerEnter----$event");
// },
// onPointerExit: (event) {
// print("onPointerExit----$event");
// },
// onPointerHover: (event) {
// print("onPointerHover----$event");
// },
child: Container(
color: Colors.pink,
child: Text("Jimi",
style: TextStyle(
color: Colors.white,
fontSize: 30
),
),
),
),
Positioned(
child: Listener(
onPointerDown: (event) {
print("red---- $event");
},
child: Container(
width: 100,
height: 100,
color: Colors.red,
child: Text("Jimi"),
),
),
)
],
),
),
);
}
}
控制台输出
我们这里先点击橙色容器,在点击一次红色容器,他们打印的结果如下。
flutter: onPointerDown----PointerDownEvent#128be(position: Offset(204.5, 403.5))
flutter: onPointerUp----PointerUpEvent#a9558(position: Offset(204.5, 403.5))
flutter: red---- PointerDownEvent#8ffdf(position: Offset(140.5, 317.0))
PointerEvent介绍
PointerEvent
是触摸、手写笔、鼠标事件的基类。
在上文中,我们知道了什么是Listener
并写了一个简单的案例,在使用案例的过程中我们的事件里面都带了一个event
参数,而所有的事件最终都是继承自PointerEvent
,那我们接下来看看event
的参数有什么作用。
PointerEvent构造函数
const PointerEvent({
this.embedderId = 0,
this.timeStamp = Duration.zero,
this.pointer = 0,
this.kind = PointerDeviceKind.touch,
this.device = 0,
this.position = Offset.zero,
Offset? localPosition,
this.delta = Offset.zero,
Offset? localDelta,
this.buttons = 0,
this.down = false,
this.obscured = false,
this.pressure = 1.0,
this.pressureMin = 1.0,
this.pressureMax = 1.0,
this.distance = 0.0,
this.distanceMax = 0.0,
this.size = 0.0,
this.radiusMajor = 0.0,
this.radiusMinor = 0.0,
this.radiusMin = 0.0,
this.radiusMax = 0.0,
this.orientation = 0.0,
this.tilt = 0.0,
this.platformData = 0,
this.synthesized = false,
this.transform,
this.original,
}) : localPosition = localPosition ?? position,
localDelta = localDelta ?? delta;
PointerEvent属性和说明
PointerEvent
的属性非常多,但在我们实际的开发过程中很少会使用到,只有在特定的情景下才会使用对应的属性。
如需要做一个全局悬浮的按钮我们会使用到position
如需要做绘图软件我们需要用到buttons
、kind
等
所以大家可以根据实际的应用场景来使用对应的属性即可,下面是我对PointerEvent
的属性进行的一个详细描述。
总共29个属性
字段 | 属性 | 描述 |
---|---|---|
embedderId | int | 标识平台事件ID |
timeStamp | Duration | 事件调度时间 |
pointer | int | 指针唯一标识符,每一次点击都会是一个新的,不会重复 |
kind | PointerDeviceKind | 指针事件的输入设备类型 |
device | int | 设备唯一标识符,在交互中会重复使用 |
position | Offset | 指针相对于全局坐标的偏移 |
localPosition | Offset | 指针相对于当前容器坐标的偏移 |
delta | Offset | 两次指针移动事件的距离 |
localDelta | Offset | 两次指针移动事件的距离(当前容器) |
buttons | int | 它是*button常量,经常与kind配合使用,做绘图的画笔软件是经常用到该属性,如果它的值为6,kind是PointerDeviceKind.invertedStylus,那么这表示一个倒置触控笔 |
down | bool | 设置当前指针是否按下 |
obscured | bool | 是否遮挡应用程序的窗口,该属性官方还没实现 |
pressure | double | 按压力度,压力传感器(如iPhone的3D Touch)中使用,取值0.0-1.0 |
pressureMin | double | 按压力度最小值 |
pressureMax | double | 按压力度最大值 |
distance | double | 检测物体与输入表面的距离 |
distanceMin | double | 限制检测物体与输入表面的距离最小值 |
distanceMax | double | 限制检测物体与输入表面的距离最大值 |
size | double | 被按下屏幕的区域大小 |
radiusMajor | double | 接触椭圆沿主轴的半径,以逻辑像素为单位 |
radiusMinor | double | 接触椭圆沿短轴的半径,以逻辑像素为单位 |
radiusMin | double | radiusMajor和radiusMinor报告的最小值 |
radiusMax | double | radiusMajor和radiusMinor报告的最大值 |
orientation | double | 检测到的物体的方向(指针移动方向),以弧度为单位 |
tilt | double | 检测到的物体的倾斜角度,以弧度为单位 |
platformData | int | 与事件关联的不透明平台特定数据 |
synthesized | bool | 设置事件是否由 Flutter 合成。 |
transform | Matrix4 | 用于从全局坐标转换此事件的转换 |
original | PointerEvent | 在任何transform之前的原始未转换PointerEvent事件 |
behavior属性
behavior
属性,它决定子组件如何响应命中测试,它的值类型为HitTestBehavior
,这是一个枚举类,有三个枚举值
HitTestBehavior.deferToChild
对子组件一个接一个的进行命中测试,如果子组件中有测试通过的,则当前组件通过,这就意味着,如果指针事件作用于子组件上时,其父级组件也肯定可以收到该事件。
HitTestBehavior.opaque
在命中测试时,将当前组件当成不透明处理(即使本身是透明的),最终的效果相当于当前Widget的整个区域都是点击区域
HitTestBehavior.translucent
点击组件透明区域时,可以对自身边界内及底部可视区域都进行命中测试,这意味着点击顶部组件透明区域时,顶部组件和底部组件都可以接收到事件
代码演示
import 'package:flutter/material.dart';
class ListenerSimpleExample extends StatefulWidget {
@override
_ListenerSimpleExampleState createState() => _ListenerSimpleExampleState();
}
class _ListenerSimpleExampleState extends State with AutomaticKeepAliveClientMixin {
@override
bool get wantKeepAlive => true;
@override
Widget build(BuildContext context) {
super.build(context);
return Scaffold(
appBar: AppBar(
title: Text("Listener"),
),
body: Center(
child: Stack(
children: [
Listener(
child: ConstrainedBox(
constraints: BoxConstraints.tight(Size(400, 200)),
child: Container(
color: Colors.greenAccent,
)
),
onPointerDown: (event) => print("绿色盒子被点击了"),
),
Listener(
child: ConstrainedBox(
constraints: BoxConstraints.tight(Size(400, 200)),
child: Center(child: Text("点击文字", style: TextStyle(
color: Colors.white,
fontSize: 30
),)),
),
onPointerDown: (event) => print("文字点击事件回调"),
behavior: HitTestBehavior.deferToChild,
// behavior: HitTestBehavior.opaque,
// behavior: HitTestBehavior.translucent,
)
],
),
),
);
}
}
当属性设置为HitTestBehavior.deferToChild控制台输出结果
我们这里演示每次都是先点击绿色盒子在点击文字,以便大家能更好的分辨出这三个属性的使用区别
flutter: 绿色盒子被点击了
flutter: 文字点击事件回调
当属性设置为HitTestBehavior.opaque控制台输出结果
flutter: 文字点击事件回调
flutter: 文字点击事件回调
当属性设置为HitTestBehavior.translucent控制台输出结果
flutter: 文字点击事件回调
flutter: 绿色盒子被点击了
flutter: 文字点击事件回调
总结
Listener
是Flutter
中比较重要的功能性组件,它主要的功能是用来监听屏幕触摸事件,事件回调可以获取对应的属性来个性化定制app功能。