Flutter
是由衆多widget
構成的UI
框架,之前的文章我們在不同的widget
之間傳遞數據是通過構造函數傳參的方式傳遞。如果嵌套的widget
過多,這麼寫不免有些麻煩且層級複雜。所以Flutter
還提供了其他方案來實現跨 widget 間數據的傳遞
,下面就介紹InheritedWidget、Notification 和 EventBus
這三種方案。
InheritedWidget
InheritedWidget
是widget
的基類,可有效地向下傳播信息。
可以理解爲子 widget
可以在任何位置獲取繼承了InheritedWidget
的父 widget
中的數據。
示例
示例中通過
構造函數
和繼承InheritedWidget
兩種方式實現父 widget :FrogColor
向子 widget:FColor
傳值。
構造函數傳值:
繼承 InheritedWidget 傳值:
- 首先定義一個類
FrogColor
繼承InheritedWidget
,並在構造方法中傳遞數據和方法:model 和 doSomeThing
class FrogColor extends InheritedWidget {
const FrogColor({
Key key,
@required this.model,
@required this.doSomeThing,
@required Widget child,
}) : assert(model != null),
assert(child != null),
super(key: key, child: child);
// 直接從 _FirstPageState 中的 State 獲取數據,這裏可指定爲泛型
final _FirstPageState model;
// 方法
final Function() doSomeThing;
// 獲取實例
static FrogColor of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<FrogColor>();
}
// 判斷是否需要更新
@override
bool updateShouldNotify(FrogColor old) => model != old.model;
}
- 在佈局中使用
FrogColor
包裹FColor
子widget
,同時傳遞data和 doSomeThing 方法
。由於上面定義的data
是_FirstPageState
所以這裏直接傳遞this
,doSomeThing
方法中我們傳遞了_changeColor
用於改變mColor
的值,並且在子佈局中能夠刷新顯示最新的color
。
- 最後我們在
FColor
中獲取到FrogColor
中的date數據
和調用doSomeThing 方法
上面的FrogColor
中model
是直接傳入_FirstPageState
,這樣耦合度會比較高
所以我們將這個對象轉爲泛型
,封裝後如下:
新建 state_provider.dart
文件:
import 'package:flutter/material.dart';
/// 使用泛型傳入數據
class IProvider<T> extends InheritedWidget {
// 數據
final T data;
// 方法
final Function() doSomeThing;
IProvider({Key key, Widget child, this.data, this.doSomeThing})
: super(key: key, child: child);
@override
bool updateShouldNotify(IProvider oldWidget) {
return data != oldWidget.data;
}
static IProvider<T> of<T>(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<IProvider<T>>();
}
}
封裝好的使用需要傳入數據類
:
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('InheritedWidget'),
),
body: IProvider<_FirstPageState>(// 傳入 _FirstPageState
data: this
doSomeThing: _changeColor,//提供修改數據的方法
child: FColor('哈哈哈'),
)
);
}
以上就是InheritedWidget
的使用,使用InheritedWidget
的方式可以降低數據和UI
界面的耦合。
直接使用InheritedWidget
不免有些麻煩,Flutter
提供了更加強大的 Provider 庫,用於實現依賴注入和狀態管理(後續瞭解之後詳細介紹)。
InheritedWidget
僅限與從父widget
向下傳遞數據。那麼從子widget
向上傳遞數據如何實現呢?Notifiation
就可以實現。
Notification
Notification
數據共享方式是從子widgetw 向上傳遞至父
widget`。
在之前的文章 Flutter 嵌套滾動 CustomScrollView 示例 中,我們使用NotificationListener
去監聽子child CustomScrollView
的滑動距離。
同理,將子widget
的數據傳遞給父widget
也可以用這個方式實現
示例
從子widget
向上發送數據,在父widget
中監聽到消息並打印在控制檯。
以上就是Notification
的使用,使用Notification
的方式可以子widget
的數據通過dispatch
發送給父widget
。
無論是 InheritedWidget
還是 Notificaiton
,都需要依賴與父子關係的widget
。
而一些場景下的數據傳遞是沒有這層關係的,
在Android
中有事件總線的方式可以很方便的實現這類場景的數據傳遞,
Flutter
中也有這樣類似的庫:EventBus
EventBus
使用
Dart Streams
進行應用程序解耦的簡單事件總線。
示例
- 引入庫
dependencies:
event_bus: ^1.1.1
- 初始化
EventBus
,並定義消息數據類
final eventBus = new EventBus();
/// 傳遞的數據
class MsgEvent{
final String msg;
MsgEvent(this.msg);
}
- 監聽
// 監聽數據變化
eventBus.on<MsgEvent>().listen((event) {
print("接收到的數據:${event.msg}");
});
- 發送數據
eventBus.fire(MsgEvent("子 widget 發送過來的數據:老頭子"));
通過以上的4步即可完成不依賴widget
的數據傳輸。
示例完整源碼
import 'package:event_bus/event_bus.dart';
import 'package:flutter/material.dart';
final eventBus = EventBus();
class FirstPage extends StatefulWidget {
@override
_FirstPageState createState() {
return _FirstPageState();
}
}
class _FirstPageState extends State<FirstPage> {
@override
void initState() {
super.initState();
// 監聽數據變化
eventBus.on<MsgEvent>().listen((event) {
print("接收到的數據:${event.msg}");
});
}
@override
void dispose() {
super.dispose();
// 釋放資源
eventBus.destroy();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('EventBus'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
// 子 widget
RaisedButton(
// 按鈕點擊時分發通知
onPressed: () {
eventBus.fire(MsgEvent("父 widget 發送過來的數據:我是你爸爸"));
},
child: Text("父 widget 發送數據"),
),
CustomChild(),
],
));
}
}
class CustomChild extends StatelessWidget {
@override
Widget build(BuildContext context) {
return RaisedButton(
// 按鈕點擊時分發通知
onPressed: () {
eventBus.fire(MsgEvent("子 widget 發送過來的數據:老頭子"));
},
child: Text("子 widget 發送數據"),
);
}
}
/// 消息數據類
class MsgEvent{
final String msg;
MsgEvent(this.msg);
}
wan~