
- Published on
- ·9 min read
比較Riverpod和BloC,以及如何選用適合的框架
- Authors

- Name
- Bert / DOTUNE
- Developer
Flutter 的狀態管理,源頭其實不在 Flutter 本身。
如果你往回追,Flutter 的 Widget-Element-RenderObject 三棵樹,設計靈感來自 React 的 Virtual DOM 和元件化思維。Web 前端那邊的狀態管理早就百花齊放——Redux、MobX、Zustand、Recoil、Jotai……每一種都代表一種對「狀態該怎麼管」的不同回答。
Flutter 這邊也走出了自己的路。從最早的 setState,到 Google 推廣的 Provider,再到現在——戰場基本上收斂到兩個選手:Riverpod 和 Bloc。
這兩個人氣最高,但設計哲學完全不同。誰比較適合你眼前的場景呢?下面和你介紹。
先搞懂 MVVM 和 MVC——前導概念
在講 Riverpod 和 Bloc 之前,先快速複習兩個被講到爛但還是很重要的架構概念:
MVC(Model-View-Controller)
最古老的 UI 架構之一。Model 管資料,View 管畫面,Controller 管協調。問題是——View 和 Controller 的界線很模糊,寫到最後 Controller 常常變垃圾場,什麼都往裡面丟。
MVVM(Model-View-ViewModel)
MVC 的進化版。ViewModel 把 Model 的資料轉成 View 可以直接吃的格式,View 只負責畫畫面,不碰業務邏輯。這個「View 和邏輯徹底分開」的想法,直接影響了後來所有 Flutter 的狀態管理方案。
Bloc 和 Riverpod,本質上都是 MVVM 的後代,但它們走的方向有區別。
Bloc——把規範寫進程式裡
Bloc 的核心是 事件驅動。UI 不直接改狀態,而是發一個 Event → Bloc 收到後處理 → 產出新的 State → UI 重繪。單向資料流,乾淨,不會回頭。

它最大的特色是:Bloc 之間不能互相通信。這是一個很刻意的設計約束。你不能在 Bloc A 裡面去聽 Bloc B 的狀態變化——要做這件事,你要嘛把兩個 Bloc 的邏輯合併,要嘛透過 Repository 層來共享資料。
為什麼要這樣限制?因為 Felix Angelov(Bloc 的作者)的設計目標很明確:讓每一個狀態轉換都是可追蹤、可記錄、可重播的。Bloc 內建了 BlocObserver,你可以全域監聽每一個 Event 進入和每一個 State 產出——這對需要審計、log、debug 的系統來說是無價的。

這也是為什麼 Bloc 常常跟 Clean Architecture 綁在一起講。Bloc → Use Case → Repository 的分層結構,搭配 Event/State 的嚴格單向流動,讓整個 App 的資料流變成一張可以畫在紙上的圖。大團隊、多人協作的時候,這張圖就是溝通的共同語言。
Bloc 最適合的場景:
- 需要事件追蹤、審計 log(金融、合規相關 App)——Event 本身就是天然的 audit trail
- 業務邏輯本身就是狀態機——表單驗證、訂單流程、多步驟操作,Event → State 的 mapping 非常直覺
- 多人協作,需要強制規範來避免每個人各寫一套風格
Bloc 的約束(也就是它讓你比較不自由的地方):
- Bloc 之間不能直接通信——不同模組需要互動時,得透過 Repository 層或合併 Bloc 來處理
- 強制 Event/State/Bloc 三層分離——結構清楚,但即使小功能也要開好幾個檔案。AI 可以幫你寫,但維護時期要跨檔案追程式碼還是比單一 provider 多一層跳轉
Riverpod——從實踐中長出來的靈活性
Riverpod 是 Rémi Rousselet 寫的。他同時也是 Provider 的作者。
Provider 的問題在於它跟 InheritedWidget 綁死——依賴 BuildContext、同類型只能有一個 provider、生命週期管理很粗糙。Rousselet 知道這些問題,但要修就必須 breaking change。所以他乾脆重寫,取名 Riverpod(Provider 的字母重組)。

Riverpod 的設計從實戰出發,不是從理論。幾個核心決定:
- 不依賴 BuildContext——你可以在純 Dart code 裡面用 provider,不限於 Widget 樹
- 編譯期安全——provider 之間的依賴關係在編譯期就檢查,不會 runtime 才炸
ProviderNotFoundException - autoDispose——provider 沒人用的時候自動釋放,不用手動管生命週期
而 Riverpod 真正跟 Bloc 拉開差距的,是資料粒度的控制。
Riverpod 的 provider 可以做到非常細——你可以為一個 int、一個 String、一個 User 物件各自宣告獨立的 provider。然後這些 provider 之間可以透過 ref.watch 互相追蹤:A 依賴 B、B 依賴 C,C 一變,A 和 B 自動跟著更新。這是一個反應式的依賴圖,不用手動寫任何的 subscribe/unsubscribe。
這也代表 Riverpod 的靈活度比 Bloc 高很多。你可以根據專案當下的需求,決定這個狀態要拆多細、要跟誰連動。不需要一開始就設計完整的 Event/State 架構。
Riverpod 最適合的場景:
- 狀態之間有複雜的依賴關係——A 變了 B 要跟著變、B 變了 C 要跟著變,
ref.watch自動處理 - 需要頻繁跨模組共享狀態——provider 不依賴 Widget 樹,任何地方都能讀寫
Riverpod 的約束(也就是它讓你比較不自由的地方):
- 靈活度高也代表沒有強制結構——同一個功能三個人可能寫出三種 provider 組合方式,需要團隊自己有紀律
ProviderObserver(didAddProvider、didUpdateProvider、didDisposeProvider)可以記錄狀態變化,但它記錄的是「值變了」,不像 Bloc 的 Event 那樣自帶「為什麼變了」的語義——如果你需要的是完整的 Event → State 審計鏈,Bloc 的設計更直接
一句話總結——知道什麼場景用什麼
| 場景 | 適合 |
|---|---|
| 狀態之間互相依賴、需要細粒度反應式更新 | Riverpod——ref.watch 自動追蹤,不用手寫訂閱 |
| 需要 Event → State 審計鏈、操作 log 可追溯 | Bloc——Event 自帶語義,Observer 記錄完整因果 |
| 業務本身就是狀態機(表單、訂單、多步驟流程) | Bloc——Event/State mapping 直接對應業務邏輯 |
| 跨模組頻繁共享狀態 | Riverpod——不依賴 BuildContext,純 Dart 可用 |
| 多人協作、需要架構強制力來統一風格 | Bloc——規範寫在框架裡,不靠口頭約定 |
| 拿 Bloc 寫需要狀態互相監聽的功能 | 痛苦,Bloc 之間不能通信,得繞路 |
| 拿 Riverpod 寫需要完整 Event 審計的系統 | 沒那麼痛苦(ProviderObserver 可以記錄),但 Event 語義要自己補 |
其他選擇如何呢
- GetX:方便,但封裝太深。出問題的時候你找不到兇手,小專案玩玩可以,大專案別碰
- MobX:反應式,概念漂亮,但在 Flutter 生態裡太小眾,出問題找不到人問
- 原生 setState:一個畫面以內的狀態,用它就好。不要為了用框架而用框架
Riverpod 和 Bloc 都是好工具。重點不是選邊站,是兩個都懂——知道每個場景該拿出哪一把,知道它的約束在哪、自由在哪。這樣你就不會在需要靈活度的地方被 Bloc 的 boilerplate 綁死,也不會在需要審計的時候發現 Riverpod 沒有 Event 讓你追溯。