在Flutter中有一個頗有爭論的部分——揪淨該怎樣狀態管理前端
我一開始堅持移動端的思路,使用MVC或者MVP等,還有的同窗支持前端思路,使用Redux或者RxDart等,也有人說Google推薦使用BLoC,還有像我翻譯的一篇文章同樣:[譯]讓我來幫你理解和選擇Flutter狀態管理方案, 使用Redux和BLoC的混合。c#
我一直在想,能不能儘可能使用在iOS上開發的方式來作,後來發現行不通,緣由是flutter的渲染方式和移動端徹底不一樣,它採用的React的思路。設計模式
移動端的UI控件能夠經過修改其屬性改變外觀,可是flutter和RN,改變樣式基本是靠從新渲染,因此想要更新內容,就要改變state,而後再經過setState()更新UI。bash
因此flutter裏更新UI,先天是割裂的,這一點和React同樣,因此就須要觀察者(或是什麼相似的)設計模式的封裝,來下降更新UI的複雜程度,減小耦合或者過多的狀態聲明。app
說了這麼多,其實就是想說,BLoC仍是值得一試的,他能解決狀態和UI揉雜在一塊兒的問題,也沒有Redux這麼重,適合用於簡單業務場景的數據同步。less
BLoC是一種設計模式,官方並無給出封裝的代碼,網上搜到的代碼大同小異(不知道是否是Google給的最佳實踐),可是他們的共同特色就是,初始值賦值上有問題,UI的初始值,沒有用BLoC的數據部分給出的初始值,只是剛好二者值相等,那當須要改變初始值的時候,就須要改不少處,這顯然不可接受。(緣由這裏就再也不贅述了)ide
剛剛也說了,BLoC的實現,使用了Stream或者RxDart,我傾向於使用Stream,由於它是內置的庫,RxDart功能我沒有評估,可是顯然針對簡單業務場景,過於重了。函數
關於BLoC的實現細節,這裏推薦你們看鑫磊的文章:Flutter | 狀態管理探索篇——BLoC(三),我在這裏不詳細說工具
BLoC的結構以下:post
我也不知道這應該歸類爲觀察者模式,仍是生產者消費者模式,總之是:
BLoC數據模塊,持有一個StreamController,來管理stream
UI須要展現數據的地方,使用StreamBuilder來監聽stream變化
當stream裏的數據變化時,就會自動刷新子UI。
須要更新數據時,好比點擊了某個按鈕,則會操做BLoC數據模塊,經過其StreamController更新裏面的數據,這樣UI就會自動刷新了。
不知不覺囉嗦了這麼多,其實這篇文章的目的是對BLoC進行封裝,使Stream類再也不暴露在業務代碼中。
Pub倉庫:
封裝分紅三部分:
RLKBaseBLoC: 數據類,存儲了任意類型的數據data(範型),還有一個改變數據的changeData方法。data供數據使用者來使用,而數據操做者使用changeData改變數據。
你能夠繼承它來定義本身的數據內容,同時增長一些方法,用於更新數據。 建立數據實例很簡單,把要存儲的初始數據傳進構造函數就能夠了:
class CountBLoC extends RLKBaseBLoC<int> {
//RLKBaseBLoC子類,增長了自加方法
CountBLoC(int data) : super(data);
increment() {
changeData(data + 1);
}
}
CountBLoC(0);//RLKBaseBLoC實例
複製代碼
RLKBloCProvider: 這個是一個Widget,保存了RLKBaseBLoC實例,example中它包住了整個MaterialApp,它的做用就是爲須要用到數據的地方提供數據來源,它只要是全部使用到RLKBaseBLoC數據的widegt的共用根節點就能夠。若是你的RLKBaseBLoC數據,只是在頁面A中有不少地方展現,那麼RLKBloCProvider只須要包住頁面A。
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return RLKBloCProvider(//包住了整個APP,由於數據要被兩個頁面使用
bloc: CountBLoC(0),//保存了RLKBaseBLoC實例
child: MaterialApp(
theme: ThemeData.light(),
home: TopPage(),
),
);
}
}
複製代碼
RLKBLoCBuilder: 當須要使用RLKBaseBLoC裏面的數據的時候,就在這個widget外圍包住RLKBLoCBuilder,它的做用就是給你提供RLKBaseBLoC數據實例,好比example中,保存RLKBaseBLoC實例的RLKBloCProvider放在了main.dart中,可是當我想在TopPage中使用RLKBaseBLoC實例的時候,是沒法直接拿到這個實例的,經過RLKBLoCBuilder,就能夠在buider方法中,以參數的形式拿到RLKBaseBLoC實例了。
Center(child: RLKBLoCBuilder(builder: (BuildContext context, int data, RLKBaseBLoC bloc) {
return Text(
'You hit me: $data times',
);
}))
複製代碼
最後,當你想更新數據的時候,一樣是經過RLKBLoCBuilder拿到RLKBaseBLoC實例,而後對數據進行操做
class _STFState extends State<UnderPage> {
@override
Widget build(BuildContext context) {
return RLKBLoCBuilder(builder: (BuildContext context, int data, RLKBaseBLoC bloc) {
CountBLoC bloc2 = bloc as CountBLoC;
return Scaffold(
appBar: AppBar(
title: Text('Under Page'),
),
body: Center(
child: Text(
"You hit me: $data times",
style: Theme.of(context).textTheme.display1,
)),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {});
bloc2.increment();//改變數據
},
child: Icon(Icons.add),
),
);
});
}
}
複製代碼
當你想要更靈活地管理狀態和UI的時候,直接使用這個package可讓你遠離複雜的Redux、RxDart、Stream等概念,只須要一個數據類和兩個容器Widegt就能夠了,數據和UI很好的分離,庫對代碼的入侵也比較少。但願能夠幫助到你。