📚 Flutter 狀態管理系列文章目錄
-
Flutter 狀態管理(setState、InheritedWidget、 Provider 、Riverpod、 BLoC / Cubit、 GetX 、MobX 、Redux)
-
setState() 使用詳解:原理及注意事項
-
InheritedWidget 組件使用及原理
-
Flutter 中 Provider 的使用、注意事項與原理解析(含代碼實戰)
-
GetX 用法詳細解析以及注意事項
-
Flutter BLoC 使用詳細解析
-
Flutter MobX 響應式原理與實戰詳解
-
Flutter Riverpod 使用詳細解析
-
Riverpod原理解析(實現一個自己的Riverpod
? 起點:為什么要了解 MobX?
作為一個剛開始接觸 Flutter 狀態管理的開發者,我最初接觸到的是 setState
、Provider
等方式。當我看到有人說 MobX 是“最像魔法的狀態管理庫”時,我開始好奇:為什么它能不寫 setState 就自動刷新 UI?
帶著這樣的疑問,我開啟了對 MobX 的深入探索。這篇文章就是我把所有探索過程系統整理后的總結,希望對你也有幫助。
🛠 一、MobX 的安裝與基本使用
MobX 的核心有三大類:
概念 | 說明 |
---|---|
Observable | 可觀察狀態,類似變量。變化時自動觸發 UI 更新 |
Action | 改變狀態的行為,所有狀態變更建議通過 action |
Reaction | 監聽變化后做副作用,比如打印日志、導航等 |
1. 添加依賴
在 pubspec.yaml
中:
dependencies:flutter:sdk: fluttermobx: ^2.2.0flutter_mobx: ^2.0.6dev_dependencies:build_runner: ^2.4.6mobx_codegen: ^2.4.0
2.創建一個 MobX Store(狀態容器)
counter_store.dart
import 'package:mobx/mobx.dart';part 'counter_store.g.dart'; // 自動生成的文件class CounterStore = _CounterStore with _$CounterStore;abstract class _CounterStore with Store { int count = 0;void increment() {count++;}
}
生成代碼命令:
flutter pub run build_runner build
# 或者自動監聽:
flutter pub run build_runner watch
3. 使用 Observer
監聽狀態
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'counter_store.dart';class CounterPage extends StatelessWidget {final CounterStore counter = CounterStore(); Widget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("MobX Counter")),body: Center(child: Observer(builder: (_) => Text('${counter.count}',style: TextStyle(fontSize: 40),),),),floatingActionButton: FloatingActionButton(onPressed: counter.increment,child: Icon(Icons.add),),);}
}
📚 二、MobX 核心注解說明
注解 | 作用 | 示例 |
---|---|---|
@observable | 可監聽變量 | @observable int count = 0; |
@action | 狀態改變函數 | @action void increment() => count++; |
@computed | 派生狀態 | String get fullName => "$firstName $lastName"; |
🧩 三、不同類型的 Observer
使用方式
類型 | 使用場景 | 是否支持 child | 是否自動跟蹤依賴 |
---|---|---|---|
Observer | 通用 UI 刷新 | ? | ? |
Observer.builder | 自定義構建器 | ? | ? |
ObserverWidget (自定義) | 可封裝為組件 | ? | ? |
Observer + child: | 子組件不依賴狀態,可優化性能 | ? | ? |
1?? 最常用的 Observer
適用于所有直接在 build()
方法中監聽 observable 狀態的場景。
? 示例:
Observer(builder: (_) => Text('${counter.count}',style: TextStyle(fontSize: 32),),
)
每當 counter.count
更新時,Text 會自動重建。
2?? Observer
+ child
優化重建
用于優化性能:當 UI 中部分元素是靜態的,可以通過 child
參數避免重建。
? 示例:
Observer(child: Icon(Icons.favorite), // 不會因為狀態變化而重建builder: (_, child) => Row(mainAxisAlignment: MainAxisAlignment.center,children: [child!,SizedBox(width: 8),Text('${counter.count}'),],),
)
?
Icon
是靜態內容,MobX 不會重復創建,節省性能。
3?? 封裝成組件:自定義 ObserverWidget
當你想將 Observer
封裝成復用組件時,可創建一個繼承自 StatelessWidget
的 Observer
控件。
? 示例:
class CountText extends StatelessWidget {final CounterStore store;const CountText({required this.store}); Widget build(BuildContext context) {return Observer(builder: (_) => Text('${store.count}', style: TextStyle(fontSize: 24)),);}
}
使用方式:
CountText(store: counterStore)
4?? ListView/GridView 中的 Observer
當你需要對列表內容做響應式刷新時,每個 item 可以使用 Observer
包裹。
? 示例:
Observer(builder: (_) => ListView.builder(itemCount: todoStore.todos.length,itemBuilder: (_, index) {final todo = todoStore.todos[index];return Observer(builder: (_) => CheckboxListTile(value: todo.done,onChanged: (_) => todo.toggleDone(),title: Text(todo.title),),);},),
)
每個
CheckboxListTile
都會監聽自己對應的todo.done
,提高效率。
🧠 補充:多層 Observer 嵌套性能說明
MobX 的響應式追蹤是“精確依賴感知”的,每個 Observer
只監聽自己用到的 @observable
,不會因為 store 中其他字段變化而重建,比傳統 Provider 更細粒度。
? 小結對比
類型 | 優勢 | 適用場景 |
---|---|---|
Observer | 最常用,自動追蹤 | 通用響應式 UI |
Observer + child | 性能優化 | 靜態元素嵌入 UI |
自定義 StatelessWidget 包裹 Observer | 組件復用 | 通用響應式組件 |
列表中嵌套 Observer | 精細追蹤每一項變化 | todo、商品列表 |
? MobX 為什么可以“這么做”——原理解析
一、核心機制:可觀察(Observable) + 自動追蹤(Tracking)
-
@observable 的變量不是普通變量,而是被 MobX 包裝成了 可追蹤對象(Atom)。
-
當
Observer
被構建時,MobX 開啟一個 “追蹤上下文”(Tracking Context):- 在
Observer.builder()
中訪問了哪些@observable
,就自動把這些變量注冊為“依賴”。
- 在
-
一旦這些依賴發生變化,MobX 會自動通知
Observer
重新構建 UI。
二、過程舉例說明
= 0;Observer(builder: (_) => Text('$count'),
)
int count
? MobX 做了什么?
-
初次 build 時讀取了
count
,MobX 會記錄下這個“讀取動作”:“這個 Observer 正在依賴 count!”
-
后續調用
count++
(即修改 observable)時:- MobX 發現
count
改了,它就通知所有依賴count
的觀察者(也就是這個Observer
)重新執行builder
,從而刷新 UI。
- MobX 發現
三、MobX 響應式模型的類比
可以把 MobX 的響應式系統類比成:
@observable
= 數據源(比如“溫度傳感器”)Observer
= 顯示模塊(比如“電子溫度計”)- MobX = 自動布線系統,知道哪個顯示器監聽哪個傳感器
只要傳感器變化,顯示模塊就刷新。開發者什么都不用手動做。
四、底層依賴追蹤的原理:Atom + Reaction
MobX 的核心類是:
類 | 說明 |
---|---|
Atom | 每個 observable 變量都是一個 Atom |
Reaction | 每個 Observer 內部注冊了一個 reaction |
Context | 每次執行 builder() 時,MobX 開啟上下文收集依賴 |
Derivation | 指的是任何依賴 observable 的函數,比如 computed 、Observer |
五、Flutter 中 UI 為什么可以重建?
因為 Observer
繼承自 StatelessWidget
,而其內部邏輯其實是這樣的(偽代碼):
class Observer extends StatelessWidget {final Reaction _reaction;Observer({required WidgetBuilder builder}) {_reaction = Reaction(() {// 當依賴的 observable 改變時,調用 setState 或 rebuild});} Widget build(BuildContext context) {// 啟動依賴收集_reaction.track(() {builder(context); // 在這個過程中讀取 observable,就會注冊依賴});}
}
? 總結一句話
MobX 可以“這么做”,是因為它在
Observer.builder
執行時自動收集依賴變量,一旦這些變量變化,MobX 會主動通知刷新 UI。你不需要寫 setState,它已經幫你自動“追蹤 + 通知 + 重建”了。
?? 五、自己手寫一個簡化版 MobX(低配實現)
太棒了!下面我就來寫一個低配版的 MobX 響應式系統實現,讓你直觀理解它的底層機制。這個版本包括:
Observable<T>
:可觀察變量Reaction
:監聽依賴并自動響應autorun()
:一個簡化版的Observer
行為- 自動依賴追蹤:MobX 的“神操作”
🧠 目標:當你讀取 observable 的時候自動注冊依賴,當你修改 observable 的時候,能觸發依賴函數。
🔧 第一步:實現全局上下文收集機制
typedef VoidCallback = void Function();class ReactiveContext {static final ReactiveContext _instance = ReactiveContext._internal();VoidCallback? _currentReaction;factory ReactiveContext() => _instance;ReactiveContext._internal();void setCurrentReaction(VoidCallback? reaction) {_currentReaction = reaction;}VoidCallback? get currentReaction => _currentReaction;
}
🔮 第二步:實現 Observable
class Observable<T> {T _value;final Set<VoidCallback> _listeners = {};Observable(this._value);T get value {// 自動依賴收集final reaction = ReactiveContext()._currentReaction;if (reaction != null) {_listeners.add(reaction);}return _value;}set value(T newValue) {if (newValue != _value) {_value = newValue;// 通知所有監聽者for (var listener in _listeners) {listener();}}}
}
🔁 第三步:實現 autorun(模擬 Observer)
void autorun(VoidCallback runner) {void wrapped() {ReactiveContext().setCurrentReaction(wrapped); // 注冊當前 reactionrunner();ReactiveContext().setCurrentReaction(null); // 清理}wrapped(); // 初始執行一次,收集依賴
}
🎯 示例:模擬 Counter 自動追蹤 + 響應
void main() {final count = Observable(0);autorun(() {print("當前 count 是:${count.value}");});// 模擬用戶操作count.value = 1; // 自動觸發 autorun 輸出count.value = 2;
}
? 輸出:
當前 count 是:0
當前 count 是:1
當前 count 是:2
🧠 總結:
MobX 組件 | 我們實現的類 |
---|---|
@observable | Observable<T> |
Observer | autorun() |
依賴追蹤 | ReactiveContext |
reaction | VoidCallback 注冊 |
💡 提升建議:
你可以進一步加上:
computed
(派生值)action
(封裝狀態更新)dispose()
(清除監聽)- 與 Flutter Widget 結合(如用
ValueListenableBuilder
或自寫ObserverWidget
)
🗺 六、MobX 響應流程圖
Observable↓ (讀取)
Reaction ← 自動注冊依賴↓
值變化?↓ 是
刷新 UI(Observer)
(你也可以參考文中附圖,完整展現了 MobX 的響應鏈條)
? 七、總結:MobX 值得用嗎?
MobX 讓狀態管理變得優雅和現代化:
- 不用寫
setState
- 不需要
notifyListeners
- 自動依賴收集
- 最細粒度響應更新
適合追求響應式編程和簡潔架構的開發者。如果你喜歡 Vue、Svelte 那種“寫了就動”的感覺,MobX 會讓你愛不釋手。
🧰 附加建議
- 推薦將每個 Store 模塊化、組件化;
- 可結合依賴注入工具如 GetIt 使用;
- 想要更復雜管理?可引入
reaction
或when
進行副作用管理。
📚 Flutter 狀態管理系列文章目錄
-
Flutter 狀態管理(setState、InheritedWidget、 Provider 、Riverpod、 BLoC / Cubit、 GetX 、MobX 、Redux)
-
setState() 使用詳解:原理及注意事項
-
InheritedWidget 組件使用及原理
-
Flutter 中 Provider 的使用、注意事項與原理解析(含代碼實戰)
-
GetX 用法詳細解析以及注意事項
-
Flutter BLoC 使用詳細解析
-
Flutter MobX 響應式原理與實戰詳解
-
Flutter Riverpod 使用詳細解析
-
Riverpod原理解析(實現一個自己的Riverpod