所有程序里界面和數(shù)據(jù)的交互都至關(guān)重要嘲叔,直接決定了整個程序的結(jié)構(gòu),選好狀態(tài)管理方案的重要性就不言而喻了
目前為止,F(xiàn)lutter 里的狀態(tài)管理有很多的實現(xiàn)方法,官方也給出了很多的案例
官方給出的參考舉例
- setState
- ChangeNotifier
- Delegate
- scoped_model -InheritedWidget
- Sigslot
- provide
- flutter-provide
- rx dart, fish redux, bloc
- EventBus
setState
最原始最基本 最重要的方式 setState,支持規(guī)模較小的程序足夠了逻淌,其它方式最終都需要調(diào)用 setState
Function callback
Flutter 內(nèi)置 ChangeNotifier, ValueNotifier 都可以認(rèn)為是類似方案
Delegate
可以認(rèn)為是多個回調(diào)函數(shù),其他語言里都有類似模式疟暖,名稱似乎來源于 Objective-C卡儒。
Sigslot
源自 Qt 里的經(jīng)典編程模式,Dart 可以輕易實現(xiàn)俐巴。這種方式在 Flutter 里可能根本不會有太多應(yīng)用
pkg:scoped_model
個人以為是最佳方案骨望,源自 Fuchsia 代碼,在其中廣泛使用欣舵,設(shè)置程序幾乎都是這個模式擎鸠,后來獨立為 package,包括注釋也只有 287 行代碼缘圈。由于 Fuchsia App 結(jié)構(gòu)都是后臺Service+前臺UI劣光,這個方案絕對是最合適的方案。使用 InheritedWidget 實現(xiàn)糟把,性能不可能更好绢涡。
pkg:provide
出自Flutter dev team,絕對的官方了遣疯,總共代碼 675行雄可。實現(xiàn)方式和 scoped_model 類似,增加 Providers缠犀,Provider 支持 Stream数苫。
flutter-provide
可以認(rèn)為這個比 provide 更早,功能更豐富辨液,實現(xiàn)依然是 InheritedWidget文判。可能不會有太廣泛使用室梅,但是在時間上有歷史意義,故列出。
rxdart, fish-redux,bloc
略……^?_?^
EventBus
分享主體:
InheritedWidget
可能對InheritedWidget 比較陌生亡鼠,但是在Flutter開發(fā)當(dāng)中赏殃,我們幾乎每個Page里都能看到的身影,并且用過它,间涵,它可以高效的將數(shù)據(jù)在Widget樹中向下傳遞仁热、共享,這在一些需要在Widget樹中共享數(shù)據(jù)的場景中非常方便勾哩,如Flutter中抗蠢,正是通過InheritedWidget來共享應(yīng)用主題(Theme)和Locale(當(dāng)前語言環(huán)境)信息的。它是自上而下的思劳。
如:
MediaQuery.of(context).size.width
Theme.of(context).snackBarTheme.actionTextColor
使用姿勢
final InheritedWidgetModel model;
//點擊+號的方法
final Function() increment;
//點擊-號的方法
final Function() reduce;
const MainInheritedWidget({Key key, Widget child, this.model, this.increment, this.reduce})
: super(key: key, child: child);
static MainInheritedWidget of(BuildContext context) {
context.dependOnInheritedWidgetOfExactType<MainInheritedWidget>();
// context.inheritFromWidgetOfExactType(MainInheritedWidget);
}
@override
bool updateShouldNotify(MainInheritedWidget oldWidget) {
return oldWidget.model != model;
}
觸發(fā)事件
//觸發(fā)迅矛,完全是自定義
MainInheritedWidget.of(context).increment();
//取數(shù)據(jù)
Text( "${MainInheritedWidget.of(context).model.count}",style: TextStyle(fontSize: 50 ,fontWeight: FontWeight.w900),),
scoped_model
短小精干的scoped_model,是以InheritedWidget為基礎(chǔ)的開發(fā),所以直接展示潜叛,它的讀取在一個函數(shù)體里去操作
class Counter extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ScopedModelDescendant<CounterModel>(
builder: (context, _, model) => ActionChip(
label: Text('${model.count}'),//可以直接取
onPressed: model.increaseCount,//可以直接改變
),
);
}
}
ChangeNotifier
通知Notification的發(fā)送是通過disPatch進(jìn)行分發(fā)的秽褒,就好像Android里面的事件分發(fā),當(dāng)NotificationListener監(jiān)聽到了通知事件威兜,這時候會走到其onNotification回調(diào)中销斟,根據(jù)回調(diào)中的返回值類型(true還是false)來決定是否還繼續(xù)向父親節(jié)點發(fā)送通知。它是自下而上椒舵。
接受數(shù)據(jù)
NotificationListener<TestNotification>(
onNotification: (notification) {
setState(() {
count = notification.count;
});
return true;
},
發(fā)送數(shù)據(jù)
TestNotification(count: count).dispatch(context);
我們需要注意的地方:
Builder(
builder: (context) {
return RaisedButton(
color: Colors.blue,
child: Text('+'),
onPressed: () {
count++;
TestNotification(count: count).dispatch(context);
},
);
},
)
為什么我們一定要用Builder 取構(gòu)建一下蚂踊?
原因是通知在分發(fā)的時候,需要一個context參數(shù)笔宿,這個參數(shù)指的是Notification監(jiān)聽的子widget的context犁钟,如果直接的話,context是根widget的措伐,這樣會導(dǎo)致監(jiān)聽不到子widget了特纤。
所以需要我們通過Builder構(gòu)建出我們子widget的context,
Provide
和scoped_model一樣侥加,Provide也是借助了InheritWidget捧存,將共享狀態(tài)放到頂層MaterialApp之上。底層部件通過Provier獲取該狀態(tài)担败,并通過混合ChangeNotifier通知依賴于該狀態(tài)的組件刷新昔穴。Provide還提供了Provide.stream,讓我們能夠以處理流的方式處理數(shù)據(jù)
直接上才藝:
void main() {
var counter = ProvideCounterModel();
var providers = Providers();
providers..provide(Provider<ProvideCounterModel>.value(counter));
runApp(ProviderNode(
child: ProvideApp(),
providers: providers,
));
接受數(shù)據(jù):
child: Provide<ProvideCounterModel>(
builder: (context, child, counter) {
return Text(
"${counter.count}",
style: TextStyle(fontSize: 50, fontWeight: FontWeight.w900),
);
},
)
發(fā)送數(shù)據(jù):
RaisedButton(
color: Colors.blue,
child: Text('+'),
onPressed: () {
Provide.value<ProvideCounterModel>(context).increment();
},
)
EventBus
在APP中提前,我們經(jīng)常會需要一個廣播機(jī)制吗货,用以跨頁面事件通知,F(xiàn)lutter中我們可以使用event_bus提供的事件總線功能來實現(xiàn)一些狀態(tài)的更新狈网,其核心是基于Dart Streams(流)宙搬;事件總線通常實現(xiàn)了訂閱者模式笨腥,訂閱者模式包含發(fā)布者和訂閱者兩種角色,可以通過事件總線來觸發(fā)事件和監(jiān)聽事件
EventBus才藝展示
import 'package:event_bus/event_bus.dart';
EventBus eventBus = new EventBus();
class CounterEvent {
int count;
CounterEvent({this.count});
}
接受數(shù)據(jù)
@override
void initState() {
super.initState();
print("initState");
subscription = eventBus.on<CounterEvent>().listen((event) {
setState(() {
count = event.count;
});
});
}
@override
void dispose() {
print("dispose");
if (subscription != null) {
subscription.cancel();
}
super.dispose();
}
發(fā)送數(shù)據(jù)
RaisedButton(
color: Colors.blue,
child: Text('+'),
onPressed: () {
count ++;
eventBus.fire(CounterEvent(count: count));
},
)