Flutter 中的事件基本上分為兩個層次:原始指標事件(Raw pointer events)與手勢(Gestures)。事件的觸發與否依 behavior
特性設定有關,不過,對於從 Web 前端開發過來的人而言,這些行為是有些陌生的,因為他們可能想要事件浮昇之類的行為。
在 Flutter 中提供了 Notification
,元件可以發送(dispatch)通知(Notification),通知會沿著樹浮昇(bubbling up),父裔中的 NotificationListener
可以獲得通知,預設情況下,通知預設會持續往上浮昇,然而父裔中某個 NotificationListener
可以阻止通知浮昇。
例如,若想模擬一下 Web 中 DOM 的 click
事件,可以自訂一個 ClickNotification
:
import 'package:flutter/material.dart';
// 自訂 NotificationListener
class ClickNotification extends Notification {
final Widget target;
ClickNotification({this.target});
}
void main() => runApp(
MaterialApp(
home: Scaffold(
// 監聽子裔的 ClickNotification
body: NotificationListener<ClickNotification> (
child: Center(
// 監聽 NotificationButton 的 ClickNotification
child: NotificationListener<ClickNotification> (
child: NotificationButton('Click me'),
onNotification: (notification) {
var button = notification.target as NotificationButton;
print('"${button.data}" Clicked');
// return true; // 傳回 true 可阻止通知浮昇
}
)
),
onNotification: (notification) {
print('bubble up');
}
),
),
)
);
class NotificationButton extends StatelessWidget {
final String data;
NotificationButton(this.data);
@override
Widget build(BuildContext context) {
return NotificationListener<ClickNotification>(
child: Builder(
builder: (context) {
return RawMaterialButton(
child: Text(data),
onPressed: () {
ClickNotification(target: this).dispatch(context);
},
);
},
),
);
}
}
在這個範例中,ClickNotification
繼承了 Notification
,主要是用來包裹 target
,也就是觸發通知的對象,在 Scaffold
的 body
部份,NotificationListener
會監聽子元件是否有通知,在其子裔中有個 NotificationButton
,會在點選按鈕時發送 ClickNotification
。
NotificationButton
的部份,主要將是用 NotificationListener
作為 Builder
的父元件,而在 RawMaterialButton
被按下時,onPressed
執行時,會建立 ClickNotification
並發送。
為什麼要用 Builder
,而不是將 RawMaterialButton
實例傳回?主要是在 context
,如果你使用以下:
...
class NotificationButton extends StatelessWidget {
final String data;
@override
Widget build(BuildContext context) {
return NotificationListener<ClickNotification>(
child: RawMaterialButton(
child: Text(data),
onPressed: () {
ClickNotification(target: this).dispatch(context);
},
),
);
}
}
那麼這時的 context
是 NotificationButton
的 Element
,也就是說,這時 NotificationListener
監聽的子裔,會是從 NotificationButton
以上發出的 ClickNotification
,這時作為 NotificationButton
的子元件 RawMaterialButton
發出的 ClickNotification
,是不會有作用的。
至於原先指定 Builder
的方式,builder
設定的函式上 context
參數,接收到的是 Builder
對應的 Element
,而 Builder
是 NotificationListener
的子元件,因此發送 ClickNotification
時,Builder
以上的父裔,預設可以收到通知。
範例若按下按鈕,主控台會顯示「“Click me” Clicked」與「bubble up」文字,如果將範例中的 return true
處註解移除,表示阻止通知浮昇,這時就只會顯示「“Click me” Clicked」。
實際上,Flutter 內建了一些 Notification
的實作,像是 DraggableScrollableNotification
、KeepAliveNotification
、LayoutChangedNotification
(子類還有 ScrollNotification
、SizeChangedLayoutNotification
)、OverscrollIndicatorNotification
等,可以用來傾聽一些系統內建的通知,必要時也可以利用一下。