前言
前面3篇我們介紹了 MobX 的概念和使用, MobX 使用起來確實很簡單,也很高效。但是, MobX 也有一個缺陷,那就是如果狀態資料要共享的時候,我們並不能像 Provider 或 Redux 那樣將狀態物件提升到共同上級元件來完成。還是以點贊和收藏按鈕的介面為例(Redux 的可以參考:Flutter 入門與實戰(六十四):這篇很長,為了效能,你忍一下 —— 從原始碼看 flutter_redux精準重新整理)。兩個按鈕是獨立的元件,點選時都會增加對應的數量,兩個元件共享一個狀態物件。
MobX 共享方式一:逐級傳遞
由於 MobX 本身不支援上級元件狀態可以被下級元件共享,因此我們想到的最簡單的方式就是將狀態物件傳遞給下級元件。
class DynamicDetailWrapper extends StatelessWidget {
final store = ShareStore();
DynamicDetailWrapper({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
//...
children: [
_PraiseButton(store: store),
_FavorButton(store: store),
],
}
}
class _FavorButton extends StatelessWidget {
final ShareStore store;
const _FavorButton({Key? key, required this.store}) : super(key: key);
@override
Widget build(BuildContext context) {
print('FavorButton');
return Container(
alignment: Alignment.center,
color: Colors.blue,
child: TextButton(
onPressed: () {
store.increamentFavor();
},
child: Observer(
builder: (context) => Text(
'收藏 ${store.favorCount}',
style: TextStyle(color: Colors.white),
),
),
style: ButtonStyle(
minimumSize: MaterialStateProperty.resolveWith(
(states) => Size((MediaQuery.of(context).size.width / 2), 60))),
),
);
}
}
// 省略點贊按鈕程式碼
複製程式碼
執行一下,效果也能達到,但是存在的問題就是如果一個狀態是全域性的,意味著需要將這個狀態物件傳遞給每一個需要該狀態的子元件,這個方式太醜陋了!那有沒有別的方法呢?
MobX 狀態共享方式二:借用 Provider
既然 MobX
不支援在上層元件定義狀態物件,通過context
來給下級元件共享,那可以給他拉一個搭檔來,顯然 Provider
是一個不錯的搭檔。我們可以將 MobX
的共享狀態物件由 Provider
來定義,然後共享給下級元件。
P.S. 發現 Provider 都已經升級到6.0.0版本了,正好試試新版本怎麼樣?
class DynamicDetailProvider extends StatelessWidget {
DynamicDetailProvider({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
//省略其他程式碼
child: Provider(
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_PraiseButton(),
_FavorButton(),
],
),
create: (context) => ShareStore(),
),
}
}
class _FavorButton extends StatelessWidget {
const _FavorButton({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
print('FavorButton');
return Container(
alignment: Alignment.center,
color: Colors.blue,
child: TextButton(
onPressed: () {
context.read<ShareStore>().increamentFavor();
},
child: Observer(
builder: (context) => Text(
'收藏 ${context.read<ShareStore>().favorCount}',
style: TextStyle(color: Colors.white),
),
),
style: ButtonStyle(
minimumSize: MaterialStateProperty.resolveWith(
(states) => Size((MediaQuery.of(context).size.width / 2), 60))),
),
);
}
}
複製程式碼
這種方式相比之前的方式就優雅多了,只是在需要共享使用 MobX
狀態物件的元件的上級使用 Provider
包裹它們,然後使用 context.read()
方法獲取狀態物件就可以了。那還有沒有更好的方式?
MobX 狀態共享方式三:使用 GetIt 容器
我們在Flutter 入門與實戰(二十七):使用 GetIt 同步不同頁面間資料曾經介紹過GetIt 容器。事實上,GetIt 容器可以儲存我們的狀態物件,在需要使用狀態物件的時候,從容器中取出來就可以了。
class DynamicDetailGetIt extends StatelessWidget {
DynamicDetailGetIt({Key? key}) : super(key: key) {
GetIt.I.registerSingleton<ShareStore>(ShareStore());
}
//省略 build 方法
}
class _FavorButton extends StatelessWidget {
const _FavorButton({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
print('FavorButton');
return Container(
alignment: Alignment.center,
color: Colors.blue,
child: TextButton(
onPressed: () {
GetIt.I.get<ShareStore>().increamentFavor();
},
child: Observer(
builder: (context) => Text(
'收藏 ${GetIt.I.get<ShareStore>().favorCount}',
style: TextStyle(color: Colors.white),
),
),
style: ButtonStyle(
minimumSize: MaterialStateProperty.resolveWith(
(states) => Size((MediaQuery.of(context).size.width / 2), 60))),
),
);
}
}
複製程式碼
可以看到,這種方式相對上面的兩種方式更為簡潔:
- 註冊單例狀態物件到 GetIt容器
- 在需要使用狀態物件的地方從GetIt容器取出即可,開箱即用!
- 無需依賴 context,可以避免 context 過於臃腫影響效能。
總結
示例原始碼請點選此處:MobX 狀態管理原始碼。本篇介紹了3種可用於MobX 狀態共享的方法,由於 MobX 無法直接利用上級元件給下級元件共享,因此需要藉助其他方式實現狀態共享。對比可以發現嗎,使用GetIt 容器的方式更為簡潔、優雅,元件間的耦合度也最低。因此,推薦在使用 MobX 進行狀態管理代理的場合使用這種方式。
我是島上碼農,微信公眾號同名,這是Flutter 入門與實戰的專欄文章,對應原始碼請看這裡:Flutter 入門與實戰專欄原始碼。
??:覺得有收穫請點個贊鼓勵一下!
?:收藏文章,方便回看哦!
?:評論交流,互相進步!