
狀態管理是任何 Flutter 應用程式的骨幹。它決定數據如何在應用程式內流動、組件之間如何溝通,並最終決定程式碼庫在不斷增長時的可維護性。
面對眾多選擇——Provider、Bloc、Riverpod、GetX、MobX——要挑選合適的方案可能令人無從入手。本指南會逐一拆解每種方案,配以實際例子,助你作出明智的決定。
PROVIDER:官方推薦之選
Provider 是 Google 推薦的狀態管理方案。它建基於 InheritedWidget 之上,提供簡單而可擴展的解決方案,適合絕大多數應用程式。
// Counter model
class CounterModel extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
}
// Setup Provider
ChangeNotifierProvider(
create: (context) => CounterModel(),
child: MyApp(),
)
// Consume in UI
Consumer<CounterModel>(
builder: (context, counter, child) {
return Text('Count: ${counter.count}');
},
)優點
- Google 官方推薦
- 簡單易學
- 文檔齊全
- 非常適合中小型應用程式
缺點
- 在大型應用程式中容易變得混亂
- 沒有內置的非同步處理
- 缺乏測試工具
- 樣板程式碼冗長
BLOC:可預測、易測試
Bloc(Business Logic Component)遵循響應式編程範式。它將展示層與業務邏輯分離,令應用程式高度可測試、行為可預測。
// Events
abstract class CounterEvent {}
class Increment extends CounterEvent {}
class Decrement extends CounterEvent {}
// States
abstract class CounterState {}
class CounterInitial extends CounterState {}
class CounterValue extends CounterState {
final int count;
CounterValue(this.count);
}
// Bloc
class CounterBloc extends Bloc<CounterEvent, CounterState> {
CounterBloc() : super(CounterInitial()) {
on<Increment>((event, emit) {
emit(CounterValue(currentCount + 1));
});
}
}
// Usage
BlocBuilder<CounterBloc, CounterState>(
builder: (context, state) {
if (state is CounterValue) {
return Text('Count: ${state.count}');
}
return Text('Count: 0');
},
)優點
- 高度可測試
- 狀態變化可預測
- 非常適合複雜應用程式
- 出色的 DevTools 支援
缺點
- 學習曲線陡峭
- 樣板程式碼繁多
- 對簡單應用程式而言過於複雜
- 需要理解 Stream 的概念
RIVERPOD:PROVIDER 2.0
Riverpod 出自 Provider 原作者之手,修正了 Provider 的許多限制,同時保持簡潔。它具備編譯期安全,而且不依賴 BuildContext。
// Provider definition
final counterProvider = StateNotifierProvider<CounterNotifier, int>(
(ref) => CounterNotifier(),
);
class CounterNotifier extends StateNotifier<int> {
CounterNotifier() : super(0);
void increment() => state++;
void decrement() => state--;
}
// Usage
class CounterWidget extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(counterProvider);
return Column(
children: [
Text('Count: $count'),
ElevatedButton(
onPressed: () => ref.read(counterProvider.notifier).increment(),
child: Text('Increment'),
),
],
);
}
}優點
- 編譯期安全
- 不依賴 BuildContext
- 內置緩存機制
- 出色的開發體驗
缺點
- 較新,社群規模較小
- 版本更新時或有破壞性改動
- 部分概念較為複雜
- 從 Provider 遷移需花一番工夫
GETX:一站式全能方案
GetX 不止於狀態管理——它還內置路由、依賴注入及國際化功能,以極少的樣板程式碼和高效能見稱。
// Controller
class CounterController extends GetxController {
var count = 0.obs;
void increment() => count++;
void decrement() => count--;
}
// Usage
class CounterWidget extends StatelessWidget {
final CounterController controller = Get.put(CounterController());
@override
Widget build(BuildContext context) {
return Column(
children: [
Obx(() => Text('Count: ${controller.count}')),
ElevatedButton(
onPressed: controller.increment,
child: Text('Increment'),
),
],
);
}
}優點
- 樣板程式碼極少
- 效能出色
- 一站式解決方案
- 容易上手
缺點
- 並非 Google 官方推薦
- 「魔法」行為可能難以捉摸
- 與 GetX 生態系統緊密耦合
- 可預測性不及其他方案
你應該選擇哪一個?
初學者之選
從 Provider 開始。它獲官方推薦、文檔齊全,學習曲線平緩。
適合:學習 Flutter、中小型應用程式、原型開發
大型應用程式
業務邏輯複雜、對可測試性要求高時,使用 Bloc。樣板程式碼的投入會在可維護性上得到回報。
適合:企業級應用程式、複雜狀態、團隊開發
現代應用程式
試試 Riverpod,享受最佳開發體驗與編譯期安全。它是 Flutter 狀態管理的未來。
適合:全新專案、重視開發體驗的團隊、大量非同步操作的應用程式
快速開發
需要極速開發、又不介意一點「魔法」時,選擇 GetX。非常適合 MVP 和獨立開發。
適合:MVP、個人專案、快速原型開發
狀態管理最佳實踐
盡量將狀態放在最接近使用它的地方。不要把所有東西都放進全域狀態。
將 UI 狀態與業務邏輯分離。載入狀態屬於 UI,業務數據屬於服務層。
盡可能讓狀態保持不可變。這樣可以減少錯誤,令除錯更輕鬆。
為狀態邏輯編寫測試。狀態管理屬於業務邏輯,理應獲得徹底測試。
不要過度設計。選擇能滿足當前需求的最簡單方案,日後隨時可以重構。


