背景
上一文深入理解InheritedWidget我們知道,InheritedWidget的資料共享方式是父Widget到子Widget的逐層傳遞,適用于父Widget狀態變更通知子widget變更的場景;那么本文講解的Notification正好相反了,資料的的流動方式就是從子Widget向上傳遞只父Widget,適用于子widget狀態變化,通知上層做相應處理的場景,
Notification用法
Notification用法分為以下幾個步驟;
- 自定義一個通知類,需要繼承自
Notification類;
class CustomNotification extends Notification {
CustomNotification(this.msg);
final String msg;
}
- 接收通知的父widget使用
NotificationListener包裝(NotificationListener實際上也是一個StatelessWidget),并實作onNotification方法
class _MyHomePageState extends State<MyHomePageWidget> {
String _msg = " 通知:\n";
@override
Widget build(BuildContext context) {
// 監聽通知
return NotificationListener<CustomNotification>(
onNotification: (notification) {
setState(() {
_msg += notification.msg;
}); // 收到子 Widget 通知,更新 msg
return false;
},
child: Container(
color: Colors.white,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
_msg,
style: TextStyle(fontSize: 15),
),
CustomChild()
], // 將子 Widget 加入到視圖樹中
),
),
));
}
}
- 子widget呼叫
Notification類的dispatch(context)方法(父widget的onNotification就會收到通知),
// 抽離出一個子 Widget 用來發通知
class CustomChild extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ElevatedButton(
// 按鈕點擊時分發通知
onPressed: () => CustomNotification("Hello\n").dispatch(context),
child: Text("Send"),
);
}
}
我們來看看效果,點擊Button三次:
不出意外,父Widget都能及時收到通知并重繪
下面我們一起通過原始碼來看看它的原理
原理
上面介紹了Notification的使用方法,現在我們深入其原始碼來了解一下它的實作原理,我們從通知的源頭觸發,通知是通過Notification的dispatch(context)方法發出的,那我們先看看dispatch(context)方法中做了什么,下面是相關原始碼:
void dispatch(BuildContext? target) {
target?.visitAncestorElements(visitAncestor);
}
dispatch(context)中呼叫了當前context的visitAncestorElements方法,我們從深入理解BuildContext一文中知道,該方法作用是訪問父級Elements,他的引數是一個回呼方法,我們這里看看他的原始碼:
@override
void visitAncestorElements(bool visitor(Element element)) {
assert(_debugCheckStateIsActiveForAncestorLookup());
Element? ancestor = _parent;
while (ancestor != null && visitor(ancestor))
// 遍歷父級element
ancestor = ancestor._parent;
}
它會遍歷父級element,直到ancestor為null或者某個遍歷回呼方法回傳false,
原始碼中的引數visitAncestor,就是Notification類中的visitAncestor方法,
// 每一個遍歷到的父級Element執行此函式
bool visitAncestor(Element element) {
//判斷當前element對應的Widget是否是NotificationListener,
//由于NotificationListener是繼承自StatelessWidget,
if (element is StatelessElement) {
final StatelessWidget widget = element.widget;
//是NotificationListener,則呼叫該NotificationListener的_dispatch方法
if (widget is NotificationListener<Notification>) {
if (widget._dispatch(this, element)) // that function checks the type dynamically
return false;
}
}
return true;
}
visitAncestor會判斷每一個遍歷到的父級Widget是否是NotificationListener,如果不是,那么久回傳true,繼續遍歷,如果是,就呼叫NotificationListener的_dispatch方法,是否繼續遍歷同樣是根據_dispatch方法的回傳值來判斷,
來看看NotificationListener的_dispatch
bool _dispatch(Notification notification, Element element) {
if (onNotification != null && notification is T) {
final bool result = onNotification!(notification);
return result == true; // so that null and false have the same effect
}
return false;
}
我們可以看到,NotificationListener的onNotification回呼最終是在_dispatch方法中執行的,是否繼續向上遍歷同樣也是根據onNotification方法的回傳值來判斷,回傳為true繼續遍歷,回傳為false終止遍歷,
到了這里整個Notification的流程就形成 了一個倍訓,
總結一下
我們其實發現Notification的原始碼其實很簡單,沒什么復雜的邏輯,Notification的核心功能也就是實作了子Widget向父Widget傳遞訊息,看原始碼同樣也是支持多個NotificationListener嵌套通知實作,
Flutter中也有很多類似的場景比如,ScrollNotification,LayoutChangedNotification等等,,,,,,
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/356137.html
標籤:其他
