Flutter中訊息傳遞
Flutter中訊息傳遞
同步更新於 gitpage xsfelvis.github.io/2019/01/12/…
前言
在native開發中訊息傳遞有多種手段,系統的廣播,第三方的eventbus等,在flutter中會有哪些手段呢?本篇將會介紹
Flutter中的訊息傳遞
InheritedWidget
InheritedWidget是Flutter中非常重要的一個功能型Widget,它可以高效的將資料在Widget樹中向下傳遞、共享,這在一些需要在Widget樹中共享資料的場景中非常方便, 我們經常通過這樣的方式,通過BuildContext
,可以拿到Theme
和MediaQuery
InheritedWidget和React中的context功能類似,和逐級傳遞資料相比,它們能實現元件跨級傳遞資料。InheritedWidget的在Widget樹中資料傳遞方向是從上到下的,這和Notification的傳遞方向正好相反。
在介紹StatefulWidget時,我們提到State物件有一個回撥didChangeDependencies
,它會在“依賴”發生變化時被Flutter Framework呼叫。而這個“依賴”指的就是是否使用了父widget中InheritedWidget的資料,如果使用了,則代表有依賴,如果沒有使用則代表沒有依賴。這種機制可以使子元件在所依賴的主題、locale等發生變化時有機會來做一些事情 比如
//得到狀態列的高度
var statusBarHeight = MediaQuery.of(context).padding.top;
//複製合併出新的主題
var copyTheme =Theme.of(context).copyWith(primaryColor: Colors.blue);
複製程式碼
使用InheritedWidget
主要涉及2部分工作量
- 建立一個繼承自 InheritedWidget 的類,使用時將其插入 Widget 樹
- 通過 BuildContext 物件提供的 inheritFromWidgetOfExactType 方法查詢 Widget 樹中最近的一個特定型別的 InheritedWidget 類的例項
共享資料類
class InheritedContext extends InheritedWidget {
//資料
final InheritedTestModel inheritedTestModel;
//點選+號的方法
final Function() increment;
//點選-號的方法
final Function() reduce;
InheritedContext({
Key key,
@required this.inheritedTestModel,
@required this.increment,
@required this.reduce,
@required Widget child,
}) : super(key: key, child: child);
static InheritedContext of(BuildContext context) {
return context.inheritFromWidgetOfExactType(InheritedContext);
}
//是否重建widget就取決於資料是否相同
@override
bool updateShouldNotify(InheritedContext oldWidget) {
return inheritedTestModel != oldWidget.inheritedTestModel;
}
}
複製程式碼
在widget中使用共享資料
class CustomerWidgetB extends StatelessWidget {
@override
Widget build(BuildContext context) {
final inheritedContext = InheritedContext.of(context);
final inheritedTestModel = inheritedContext.inheritedTestModel;
return new Padding(
padding: const EdgeInsets.only(left: 10.0, top: 10.0, right: 10.0),
child: new Text(
'當前count:${inheritedTestModel.count}',
style: new TextStyle(fontSize: 20.0),
),
);
}
}
複製程式碼
在樹中從上向下傳遞
@override
Widget build(BuildContext context) {
return new InheritedContext(
inheritedTestModel: inheritedTestModel,
increment: _incrementCount,
reduce: _reduceCount,
child: new Scaffold(
appBar: new AppBar(
title: new Text('InheritedWidgetTest'),
),
body: new Column(
children: <Widget>[
new Padding(
padding: const EdgeInsets.only(left: 10.0, top: 10.0, right: 10.0),
child: new Text('我們常使用的\nTheme.of(context).textTheme\nMediaQuery.of(context).size等\n就是通過InheritedWidget實現的',
style: new TextStyle(fontSize: 20.0),),
),
new CustomerWidgetA(),
new CustomerWidgetB(),
new CustomerWidgetC(),
],
),
));
}
複製程式碼
具體程式碼可以檢視
常見錯誤
MediaQuery.of() called with a context that does not contain a MediaQuery
見ss stackoverflow.com/questions/5… You need a MaterialApp
or a WidgetsApp
arround your widget. They provide the MediaQuery
. When you call .of(context)
Notification
notification 跟inheritedWidget恰恰相反,是從 子節點向父節點發送訊息在Widget樹中,每一個節點都可以分發通知,通知會沿著當前節點(context)向上傳遞,所有父節點都可以通過NotificationListener來監聽通知,Flutter中稱這種通知由子向父的傳遞為“通知冒泡”(Notification Bubbling)。Flutter中很多地方使用了通知,如可滾動(Scrollable) Widget中滑動時就會分發ScrollNotification,而Scrollbar正是通過監聽ScrollNotification來確定滾動條位置的。
使用Notification
- 自定義通知 要繼承自Notification類
- 分發通知 Notification有一個
dispatch(context)
方法,它是用於分發通知的,我們說過context實際上就是操作Element的一個介面,它與Element樹上的節點是對應的,通知會從context對應的Element節點向上冒泡。
class CustomerNotification extends Notification {
CustomerNotification(this.msg);
final String msg;
}
複製程式碼
class NotificationStudyState extends State<NotificationStudy> {
String _msg = "";
@override
Widget build(BuildContext context) {
//監聽通知
return NotificationListener<CustomerNotification>(
onNotification: (notification) {
setState(() {
_msg += notification.msg + " ";
});
},
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
// RaisedButton(
// onPressed: () => CustomerNotification("Hello NotificationStudy").dispatch(context),
// child: Text("Send Notification"),
// ),
Builder(
builder: (context) {
return RaisedButton(
//按鈕點選時分發通知
onPressed: () => CustomerNotification("Hello NotificationStudy").dispatch(context),
child: Text("Send Notification"),
);
},
),
Text(_msg)
],
),
),
);
}
}
複製程式碼
注意:程式碼中註釋的部分是不能正常工作的,因為這個context
是根Context,而NotificationListener是監聽的子樹,所以我們通過Builder
來構建RaisedButton,來獲得按鈕位置的context。
以上程式碼 參見 github.com/xsfelvis/le…
事件匯流排
目前在已經有了一個eventbus外掛
pub.flutter-io.cn/packages/ev…
用法跟原生eventbus類似
- 引入
import 'package:event_bus/event_bus.dart';
EventBus eventBus = new EventBus();
複製程式碼
- 監聽事件
eventBus.on().listen((event) {
print(event.runtimeType);
});
複製程式碼
- 傳送事件
eventBus.fire(event);
複製程式碼