Listener介紹
Listener它是主要的功能是用來(lái)監(jiān)聽(tīng)屏幕觸摸事件,取決于它的子組件區(qū)域范圍,比如按下、移動(dòng)、抬起、取消等操作時(shí)可以添加監(jiān)聽(tīng)。
我們知道Flutter組件只有按鈕才會(huì)有事件,那么如果我需要在文字或者某個(gè)容器上添加事件那我就需要借助Listener
視頻教程地址
在什么情況下使用Listener?
Listener常用于當(dāng)手指滑動(dòng)屏幕時(shí)進(jìn)行隱藏鍵盤或者下拉刷新、上拉加載時(shí)進(jìn)行事件監(jiān)聽(tīng)。
一般在實(shí)際的開發(fā)過(guò)程中我們很少會(huì)用到Listener來(lái)監(jiān)聽(tīng)手勢(shì),一般都是通過(guò)GestureDetector來(lái)進(jìn)行監(jiān)聽(tīng)或者使用MouseRegion來(lái)監(jiān)聽(tīng)鼠標(biāo)的事件,而MouseRegion常用于web開發(fā)中,GestureDetector常用于app。
Listener原理
- 當(dāng)指針按下時(shí),F(xiàn)lutter會(huì)對(duì)應(yīng)用程序執(zhí)行命中測(cè)試(Hit Test),以確定指針與屏幕接觸的位置存在哪些組件(widget)
- 指針按下事件(以及該指針的后續(xù)事件)然后被分發(fā)到由命中測(cè)試發(fā)現(xiàn)的最內(nèi)部的組件
- 事件會(huì)沿著最內(nèi)部的組件向組件樹的根冒泡分發(fā)
- 沒(méi)有機(jī)制取消或停止“冒泡”過(guò)程
Listener構(gòu)造函數(shù)
我們經(jīng)常使用的回調(diào)函數(shù)主要有三個(gè)
- 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屬性和說(shuō)明
| 字段 | 屬性 | 描述 |
|---|---|---|
| onPointerDown | PointerDownEventListener | 指針按下時(shí)觸發(fā)回調(diào) |
| onPointerMove | PointerMoveEventListener | 指針移動(dòng)時(shí)觸發(fā)回調(diào) |
| onPointerUp | PointerUpEventListener | 指針移開時(shí)觸發(fā)回調(diào) |
| onPointerSignal | PointerSignalEventListener | 當(dāng)指針信號(hào)出現(xiàn)時(shí)調(diào)用 |
| onPointerCancel | PointerCancelEventListener | 指針取消時(shí)觸發(fā)回調(diào) |
| onPointerEnter | PointerEnterEventListener | 當(dāng)指針進(jìn)入?yún)^(qū)域時(shí)回調(diào)(已廢棄) |
| onPointerExit | PointerExitEventListener | 當(dāng)指針移出區(qū)域時(shí)回調(diào)(已廢棄) |
| onPointerHover | PointerHoverEventListener | 當(dāng)沒(méi)有觸發(fā) [onPointerDown] 的指針改變時(shí)調(diào)用 |
| behavior | HitTestBehavior | 在命中測(cè)試期間如何表現(xiàn) |
| child | Widget | 子組件 |
Listener基本使用
我們這里主要是針對(duì)onPointerDown、onPointerMove、onPointerUp 進(jìn)行演示,因?yàn)槲覀冊(cè)谄綍r(shí)的開發(fā)過(guò)程中最常用到的屬性就是這三個(gè),而且其他的屬性也都被廢棄掉了。
import 'package:flutter/material.dart';
class ListenerExample extends StatefulWidget {
@override
_ListenerExampleState createState() => _ListenerExampleState();
}
class _ListenerExampleState extends State<ListenerExample> {
@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"),
),
),
)
],
),
),
);
}
}
控制臺(tái)輸出
我們這里先點(diǎn)擊橙色容器,在點(diǎn)擊一次紅色容器,他們打印的結(jié)果如下。
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是觸摸、手寫筆、鼠標(biāo)事件的基類。
在上文中,我們知道了什么是Listener并寫了一個(gè)簡(jiǎn)單的案例,在使用案例的過(guò)程中我們的事件里面都帶了一個(gè)event參數(shù),而所有的事件最終都是繼承自PointerEvent,那我們接下來(lái)看看event的參數(shù)有什么作用。
PointerEvent構(gòu)造函數(shù)
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屬性和說(shuō)明
PointerEvent的屬性非常多,但在我們實(shí)際的開發(fā)過(guò)程中很少會(huì)使用到,只有在特定的情景下才會(huì)使用對(duì)應(yīng)的屬性。
如需要做一個(gè)全局懸浮的按鈕我們會(huì)使用到position
如需要做繪圖軟件我們需要用到buttons、kind等
所以大家可以根據(jù)實(shí)際的應(yīng)用場(chǎng)景來(lái)使用對(duì)應(yīng)的屬性即可,下面是我對(duì)PointerEvent的屬性進(jìn)行的一個(gè)詳細(xì)描述。
總共29個(gè)屬性
| 字段 | 屬性 | 描述 |
|---|---|---|
| embedderId | int | 標(biāo)識(shí)平臺(tái)事件ID |
| timeStamp | Duration | 事件調(diào)度時(shí)間 |
| pointer | int | 指針唯一標(biāo)識(shí)符,每一次點(diǎn)擊都會(huì)是一個(gè)新的,不會(huì)重復(fù) |
| kind | PointerDeviceKind | 指針事件的輸入設(shè)備類型 |
| device | int | 設(shè)備唯一標(biāo)識(shí)符,在交互中會(huì)重復(fù)使用 |
| position | Offset | 指針相對(duì)于全局坐標(biāo)的偏移 |
| localPosition | Offset | 指針相對(duì)于當(dāng)前容器坐標(biāo)的偏移 |
| delta | Offset | 兩次指針移動(dòng)事件的距離 |
| localDelta | Offset | 兩次指針移動(dòng)事件的距離(當(dāng)前容器) |
| buttons | int | 它是*button常量,經(jīng)常與kind配合使用,做繪圖的畫筆軟件是經(jīng)常用到該屬性,如果它的值為6,kind是PointerDeviceKind.invertedStylus,那么這表示一個(gè)倒置觸控筆 |
| down | bool | 設(shè)置當(dāng)前指針是否按下 |
| obscured | bool | 是否遮擋應(yīng)用程序的窗口,該屬性官方還沒(méi)實(shí)現(xiàn) |
| pressure | double | 按壓力度,壓力傳感器(如iPhone的3D Touch)中使用,取值0.0-1.0 |
| pressureMin | double | 按壓力度最小值 |
| pressureMax | double | 按壓力度最大值 |
| distance | double | 檢測(cè)物體與輸入表面的距離 |
| distanceMin | double | 限制檢測(cè)物體與輸入表面的距離最小值 |
| distanceMax | double | 限制檢測(cè)物體與輸入表面的距離最大值 |
| size | double | 被按下屏幕的區(qū)域大小 |
| radiusMajor | double | 接觸橢圓沿主軸的半徑,以邏輯像素為單位 |
| radiusMinor | double | 接觸橢圓沿短軸的半徑,以邏輯像素為單位 |
| radiusMin | double | radiusMajor和radiusMinor報(bào)告的最小值 |
| radiusMax | double | radiusMajor和radiusMinor報(bào)告的最大值 |
| orientation | double | 檢測(cè)到的物體的方向(指針移動(dòng)方向),以弧度為單位 |
| tilt | double | 檢測(cè)到的物體的傾斜角度,以弧度為單位 |
| platformData | int | 與事件關(guān)聯(lián)的不透明平臺(tái)特定數(shù)據(jù) |
| synthesized | bool | 設(shè)置事件是否由 Flutter 合成。 |
| transform | Matrix4 | 用于從全局坐標(biāo)轉(zhuǎn)換此事件的轉(zhuǎn)換 |
| original | PointerEvent | 在任何transform之前的原始未轉(zhuǎn)換PointerEvent事件 |
behavior屬性
behavior屬性,它決定子組件如何響應(yīng)命中測(cè)試,它的值類型為HitTestBehavior,這是一個(gè)枚舉類,有三個(gè)枚舉值
HitTestBehavior.deferToChild
對(duì)子組件一個(gè)接一個(gè)的進(jìn)行命中測(cè)試,如果子組件中有測(cè)試通過(guò)的,則當(dāng)前組件通過(guò),這就意味著,如果指針事件作用于子組件上時(shí),其父級(jí)組件也肯定可以收到該事件。
HitTestBehavior.opaque
在命中測(cè)試時(shí),將當(dāng)前組件當(dāng)成不透明處理(即使本身是透明的),最終的效果相當(dāng)于當(dāng)前Widget的整個(gè)區(qū)域都是點(diǎn)擊區(qū)域
HitTestBehavior.translucent
點(diǎn)擊組件透明區(qū)域時(shí),可以對(duì)自身邊界內(nèi)及底部可視區(qū)域都進(jìn)行命中測(cè)試,這意味著點(diǎn)擊頂部組件透明區(qū)域時(shí),頂部組件和底部組件都可以接收到事件
代碼演示
import 'package:flutter/material.dart';
class ListenerSimpleExample extends StatefulWidget {
@override
_ListenerSimpleExampleState createState() => _ListenerSimpleExampleState();
}
class _ListenerSimpleExampleState extends State<ListenerSimpleExample> 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("綠色盒子被點(diǎn)擊了"),
),
Listener(
child: ConstrainedBox(
constraints: BoxConstraints.tight(Size(400, 200)),
child: Center(child: Text("點(diǎn)擊文字", style: TextStyle(
color: Colors.white,
fontSize: 30
),)),
),
onPointerDown: (event) => print("文字點(diǎn)擊事件回調(diào)"),
behavior: HitTestBehavior.deferToChild,
// behavior: HitTestBehavior.opaque,
// behavior: HitTestBehavior.translucent,
)
],
),
),
);
}
}
當(dāng)屬性設(shè)置為HitTestBehavior.deferToChild控制臺(tái)輸出結(jié)果
我們這里演示每次都是先點(diǎn)擊綠色盒子在點(diǎn)擊文字,以便大家能更好的分辨出這三個(gè)屬性的使用區(qū)別
flutter: 綠色盒子被點(diǎn)擊了
flutter: 文字點(diǎn)擊事件回調(diào)
當(dāng)屬性設(shè)置為HitTestBehavior.opaque控制臺(tái)輸出結(jié)果
flutter: 文字點(diǎn)擊事件回調(diào)
flutter: 文字點(diǎn)擊事件回調(diào)
當(dāng)屬性設(shè)置為HitTestBehavior.translucent控制臺(tái)輸出結(jié)果
flutter: 文字點(diǎn)擊事件回調(diào)
flutter: 綠色盒子被點(diǎn)擊了
flutter: 文字點(diǎn)擊事件回調(diào)
總結(jié)
Listener是Flutter中比較重要的功能性組件,它主要的功能是用來(lái)監(jiān)聽(tīng)屏幕觸摸事件,事件回調(diào)可以獲取對(duì)應(yīng)的屬性來(lái)個(gè)性化定制app功能。