很多人学 BLoC,是从
BlocProvider / BlocBuilder开始的。
但真正决定你能不能用好 BLoC 的,从来不是这些 API,
而是:你有没有把系统当成一个状态机来看。
一、为什么会有 BLoC?
在最原始的 Flutter / Android 开发中,我们通常这样改状态:
setState(() { loading = true; data = ... error = null; });这在小项目里没问题,但项目一复杂,三个问题必然出现:
❌ 1. 状态散落
loading / error / empty / submitting / success 混在页面里。
❌ 2. 变化不可控
谁在什么时候改了什么,只能靠人脑跟。
❌ 3. 流程无法建模
登录流、支付流、设备控制流,本质是状态机,代码却是变量拼凑。
本质问题只有一句话:
👉系统没有“状态模型”,只有“状态变量”。
二、BLoC 想解决的根问题
BLoC 的目标不是“把状态挪个地方放”,
而是:
👉 把“状态变化本身”提升为系统的一等公民。
也就是说,系统必须明确三件事:
系统有哪些状态(State)
系统会收到哪些事件(Event)
在不同状态下,收到事件会如何演进
这在系统层面叫一个词:
👉状态机(State Machine)
三、BLoC 的最小世界观(脱离 Flutter)
在任何 BLoC 系统中,只有三个角色:
Event:发生了什么 State:系统现在处于什么阶段 Bloc :状态机(决定如何迁移)👉 系统的所有变化,必须经过:
(Event + 当前 State) -> 新 State这就是 BLoC 的全部本质。
四、从 0 手搓一个“迷你 BLoC”
先完全不看 flutter_bloc,我们用最原始方式写一个 BLoC。
1️⃣ 定义 State(系统阶段)
abstract class CounterState {} class Idle extends CounterState {} class Counting extends CounterState { final int value; Counting(this.value); }注意:
这里不是“数据类”,而是在描述:
👉 系统有哪些合法阶段。
2️⃣ 定义 Event(系统输入)
abstract class CounterEvent {} class Increment extends CounterEvent {} class Reset extends CounterEvent {}Event 不是“函数”,
Event 是:
👉系统允许的变化来源。
3️⃣ 定义 Bloc(状态机核心)
class CounterBloc { CounterState state = Idle(); final _controller = StreamController<CounterState>.broadcast(); Stream<CounterState> get stream => _controller.stream; void add(CounterEvent event) { if (state is Idle && event is Increment) { state = Counting(1); } else if (state is Counting && event is Increment) { final v = (state as Counting).value + 1; state = Counting(v); } else if (event is Reset) { state = Idle(); } _controller.add(state); } }你现在已经拥有了一个完整 BLoC:
- 只有 Event 能触发变化
- 只有 Bloc 能修改 State
- 所有变化路径都集中在一个地方
👉 这已经是一个状态机系统。
五、你已经完成了关键转变
从这一刻开始:
❌ UI 不能直接改状态
❌ 业务不能随便改变量
只能:
bloc.add(Event)
👉 系统从“变量驱动”,升级为:
👉状态迁移驱动
六、为什么这一步在复杂系统里极其重要?
因为现在系统变成了:
有限状态 + 有限事件 + 明确迁移带来的直接能力是:
✅ 所有状态可枚举
✅ 所有入口可枚举
✅ 所有路径可推理
✅ 所有流程可测试
✅ 所有异常分支可约束
这类系统最怕的不是“写得慢”,
而是:
👉状态乱跳、流程不可控、问题不可复现。
而 BLoC 正是为此而生。
个人理解: event 触发,一个入口,修改状态。
七、再把 Flutter 接进来(只是外壳)
flutter_bloc只是把刚才这套机制:
- 封装成类 Bloc
- 帮你管理 Stream
- 帮你绑定 Widget 生命周期
例如:
BlocBuilder<CounterBloc, CounterState>( builder: (context, state) { if (state is Idle) return Text("0"); if (state is Counting) return Text("${state.value}"); return SizedBox(); }, )context.read<CounterBloc>().add(Increment());
框架只是“接 UI”,
状态机模型完全没变。
八、如何用 BLoC 正确建模真实业务
一个真实业务 BLoC,通常不是“一个 int”,而是:
👉 一个流程系统
例如“列表页面”:
State 可能是:
Initial
Loading
Refreshing
Data
Empty
Error
NoMore
Event 可能是:
Load
Refresh
LoadMore
Retry
Bloc 做的事就是:
明确写出:在什么状态下,收到什么事件,系统走向什么新状态。
这一步是系统设计,而不是写 UI。
九、BLoC 在架构坐标系中的真实定位
从系统角度看:
- Riverpod 擅长:结构系统(依赖 / 生命周期 / 自动传播)
- BLoC 擅长:行为系统(流程 / 状态演进 / 约束)
BLoC 更接近:
- Redux / MVI
- 后端事件驱动系统
- 协议状态机
- 设备控制系统
它解决的不是“状态放哪”,
而是:
👉系统如何演进。
十、什么时候该优先考虑 BLoC?
当你的问题主要是:
- 状态阶段多
- 流程复杂
- 异常分支多
- 行为必须可预测
- 系统必须可回放/可审计
👉 BLoC 非常合适。
当你的问题主要是:
- 依赖复杂
- 资源生命周期复杂
- 数据自动联动
- 组合关系多
👉 更偏 Riverpod 路线。
十一、总结一句话(这篇文章的核心)
BLoC 不是“Flutter 写法”,
BLoC 是一种显式状态机架构。它把“状态变化”,从代码细节,提升为系统模型。