2026 年 Flutter 狀態管理方案比較:Provider vs Riverpod vs Bloc

前言

狀態管理一直是 Flutter 開發者最熱烈討論的話題之一。從最早的 setState 到現在百花齊放的各種方案,每隔一段時間社群就會出現「到底該用哪個?」的討論。身為一個獨立開發者,我在過去幾年的專案中實際使用過 Provider、Riverpod 和 Bloc,今天就來分享我的實際體驗和觀察。

Provider:入門首選,簡單直覺

Provider 是 Flutter 官方推薦的狀態管理方案之一,由 Remi Rousselet 開發。它的核心概念很簡單:透過 InheritedWidget 的封裝,讓你可以在 Widget Tree 中輕鬆地傳遞和監聽資料變化。

// 定義一個簡單的 Counter Provider
class CounterModel extends ChangeNotifier {
  int _count = 0;
  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();
  }
}

// 在頂層註冊
ChangeNotifierProvider(
  create: (_) => CounterModel(),
  child: MyApp(),
)

// 在子 Widget 中使用
final counter = context.watch<CounterModel>();
Text('${counter.count}')

優點:

  • 學習曲線平緩,概念簡單易懂
  • 官方文件完整,社群資源豐富
  • 與 Flutter 的 Widget 系統緊密結合

缺點:

  • 依賴 BuildContext,在非 Widget 層級存取不方便
  • 型別安全性不夠嚴謹,容易遇到 runtime error
  • 巢狀多個 Provider 時程式碼可讀性下降

我在早期的小型專案中大量使用 Provider,對於簡單的應用來說綽綽有餘。但當專案規模變大,我開始感受到它的侷限性。

Riverpod:Provider 的進化版

Riverpod 同樣出自 Remi Rousselet 之手,可以把它看作 Provider 的「重新設計版」。它解決了 Provider 的許多痛點,最大的突破是完全不依賴 BuildContext

// 定義一個 Riverpod Provider
@riverpod
class Counter extends _$Counter {
  @override
  int build() => 0;

  void increment() {
    state++;
  }
}

// 在 Widget 中使用(ConsumerWidget)
class CounterPage extends ConsumerWidget {
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);
    return Text('$count');
  }
}

優點:

  • 不依賴 BuildContext,可以在任何地方存取狀態
  • 編譯期就能檢查型別錯誤,大幅減少 runtime error
  • Code generation 讓語法更簡潔
  • 內建支援非同步狀態(AsyncValue
  • Provider 之間的依賴關係管理非常優雅

缺點:

  • 學習曲線比 Provider 稍高
  • Code generation 需要額外設定 build_runner
  • 版本迭代快,從 v1 到 v2 的遷移需要花時間

我目前的新專案幾乎都採用 Riverpod。尤其是它處理非同步資料的方式,搭配 AsyncValuewhen 方法,可以非常優雅地處理 loading、error 和 data 三種狀態:

@riverpod
Future<List<AppInfo>> fetchApps(FetchAppsRef ref) async {
  final response = await http.get(Uri.parse('/api/apps'));
  return parseApps(response.body);
}

// 使用時
final appsAsync = ref.watch(fetchAppsProvider);
return appsAsync.when(
  data: (apps) => ListView.builder(...),
  loading: () => CircularProgressIndicator(),
  error: (err, stack) => Text('錯誤:$err'),
);

Bloc:企業級首選,架構嚴謹

Bloc(Business Logic Component)由 Felix Angelov 開發,採用事件驅動的架構模式。它強制將業務邏輯與 UI 完全分離,適合需要嚴格架構規範的大型專案。

// 定義事件
sealed class CounterEvent {}
class Increment extends CounterEvent {}
class Decrement extends CounterEvent {}

// 定義 Bloc
class CounterBloc extends Bloc<CounterEvent, int> {
  CounterBloc() : super(0) {
    on<Increment>((event, emit) => emit(state + 1));
    on<Decrement>((event, emit) => emit(state - 1));
  }
}

// 在 Widget 中使用
BlocBuilder<CounterBloc, int>(
  builder: (context, count) => Text('$count'),
)

優點:

  • 架構清晰,業務邏輯與 UI 完全分離
  • 事件驅動模式讓狀態變化可追蹤、可測試
  • 適合多人團隊協作,降低程式碼衝突
  • 豐富的生態系:bloc_testhydrated_blocreplay_bloc

缺點:

  • 樣板程式碼(boilerplate)較多
  • 簡單功能也需要定義 Event、State、Bloc 三個部分
  • 學習曲線最高,需要理解事件驅動的思維模式

Bloc 在我做外包專案或與團隊合作時特別好用。它的架構規範讓不同開發者寫出來的程式碼風格一致,也讓程式碼審查變得更容易。

實際比較:我怎麼選?

面向 Provider Riverpod Bloc
學習難度 ⭐ 低 ⭐⭐ 中 ⭐⭐⭐ 高
樣板程式碼 中等
型別安全 普通 優秀 優秀
測試性 中等 優秀 優秀
適合專案規模 小型 中小型 中大型
非同步處理 需額外處理 內建支援 需搭配 Event

我的推薦

  • 個人小專案 / 學習階段:從 Provider 開始,理解狀態管理的基本概念
  • 獨立開發者的正式專案:Riverpod 是目前最平衡的選擇,它的開發體驗和程式碼品質都很出色
  • 團隊協作 / 企業專案:Bloc 的嚴格架構能確保程式碼一致性,長期維護更容易

不要忽略的其他選項

除了上述三個主流方案,社群中也有其他值得關注的選擇:

  • GetX:功能全面但爭議較大,不太符合 Flutter 的設計哲學
  • MobX:來自 React 生態的響應式方案,適合喜歡 observable 模式的開發者
  • signals:受到前端框架啟發的新方案,值得觀察後續發展

結語

沒有「最好」的狀態管理方案,只有「最適合」的。我的建議是:先理解每個方案的核心概念和取捨,再根據專案的規模、團隊的經驗和實際需求做出選擇。最重要的是,不要花太多時間在選擇上而忘了真正重要的事——把產品做出來。

如果你跟我一樣是獨立開發者,我會建議直接從 Riverpod 開始。它在開發效率和程式碼品質之間取得了很好的平衡,也是目前 Flutter 社群中成長最快的方案之一。