BLoC 从 0 到 1:先理解“状态机”,再谈 Flutter

很多人学 BLoC,是从 BlocProvider / BlocBuilder 开始的。

但真正决定你能不能用好 BLoC 的,从来不是这些 API,

而是:你有没有把系统当成一个状态机来看。

一、为什么会有 BLoC?

在最原始的 Flutter / Android 开发中,我们通常这样改状态:

Dart 复制代码
setState(() {
  loading = true;
  data = ...
  error = null;
});

这在小项目里没问题,但项目一复杂,三个问题必然出现:

❌ 1. 状态散落

loading / error / empty / submitting / success 混在页面里。

❌ 2. 变化不可控

谁在什么时候改了什么,只能靠人脑跟。

❌ 3. 流程无法建模

登录流、支付流、设备控制流,本质是状态机,代码却是变量拼凑。

本质问题只有一句话:

👉 系统没有"状态模型",只有"状态变量"。

二、BLoC 想解决的根问题

BLoC 的目标不是"把状态挪个地方放",

而是:

👉 把"状态变化本身"提升为系统的一等公民。

也就是说,系统必须明确三件事:

  • 系统有哪些状态(State)

  • 系统会收到哪些事件(Event)

  • 在不同状态下,收到事件会如何演进

这在系统层面叫一个词:

👉 状态机(State Machine)

三、BLoC 的最小世界观(脱离 Flutter)

在任何 BLoC 系统中,只有三个角色:

Dart 复制代码
Event:发生了什么
State:系统现在处于什么阶段
Bloc :状态机(决定如何迁移)

👉 系统的所有变化,必须经过:

Dart 复制代码
(Event + 当前 State) -> 新 State

这就是 BLoC 的全部本质。

四、从 0 手搓一个"迷你 BLoC"

先完全不看 flutter_bloc,我们用最原始方式写一个 BLoC。

1️⃣ 定义 State(系统阶段)

Dart 复制代码
abstract class CounterState {}

class Idle extends CounterState {}

class Counting extends CounterState {
  final int value;
  Counting(this.value);
}

注意:

这里不是"数据类",而是在描述:

👉 系统有哪些合法阶段

2️⃣ 定义 Event(系统输入)

Dart 复制代码
abstract class CounterEvent {}

class Increment extends CounterEvent {}

class Reset extends CounterEvent {}

Event 不是"函数",

Event 是:

👉 系统允许的变化来源。

3️⃣ 定义 Bloc(状态机核心)

Dart 复制代码
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)

👉 系统从"变量驱动",升级为:

👉 状态迁移驱动

六、为什么这一步在复杂系统里极其重要?

因为现在系统变成了:

Dart 复制代码
有限状态 + 有限事件 + 明确迁移

带来的直接能力是:

  • ✅ 所有状态可枚举

  • ✅ 所有入口可枚举

  • ✅ 所有路径可推理

  • ✅ 所有流程可测试

  • ✅ 所有异常分支可约束

这类系统最怕的不是"写得慢",

而是:

👉 状态乱跳、流程不可控、问题不可复现。

而 BLoC 正是为此而生。

个人理解: event 触发,一个入口,修改状态。

七、再把 Flutter 接进来(只是外壳)

flutter_bloc 只是把刚才这套机制:

  • 封装成类 Bloc
  • 帮你管理 Stream
  • 帮你绑定 Widget 生命周期

例如:

Dart 复制代码
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 是一种显式状态机架构

它把"状态变化",从代码细节,提升为系统模型。

相关推荐
Flutter OH14 小时前
Flutter OH平台 DFX 问题定位导航
flutter
恋猫de小郭16 小时前
AI 时代,也许你的 Flutter 需要一套 Dartastic OpenTelemetry 监控
android·前端·flutter
等....17 小时前
Flutter创建
flutter
Privasa-隐私实验室17 小时前
跨端技术选型|Flutter搭建隐私加密App,安卓与iOS双端差异适配实战
android·笔记·安全·flutter·ios·隐私安全·aes-256
Flutter OH1 天前
Flutter OH 外接纹理问题定位指南
flutter
未来猫咪花1 天前
Everything is ViewModel:让状态管理回到对象世界
android·flutter·ios
Flutter OH1 天前
Flutter OH 日志抓取与过滤指南
flutter
Flutter OH1 天前
Flutter OH 内存与 GPU 问题定位指南
flutter
Flutter OH1 天前
Flutter OH 多设备适配问题定位指南
flutter
律宏阔1 天前
Flutter Hooks 与 flutter_map_animations 冲突:第二次地图动画报错的解决方案
flutter