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 是一种显式状态机架构

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

相关推荐
小雨下雨的雨2 小时前
Flutter鸿蒙共赢——秩序与未知的共鸣:彭罗斯瓷砖在鸿蒙律动中的数字重构
flutter·华为·重构·交互·harmonyos·鸿蒙系统
行者962 小时前
Flutter适配OpenHarmony:个人中心
flutter·harmonyos·鸿蒙
小雨下雨的雨2 小时前
Flutter鸿蒙共赢——生命之痕:图灵图样与反应-扩散方程的生成美学
分布式·flutter·华为·交互·harmonyos·鸿蒙系统
摘星编程2 小时前
Flutter for OpenHarmony 实战:Dialog 对话框详解
flutter·wpf
LawrenceLan2 小时前
Flutter 零基础入门(十三):late 关键字与延迟初始化
开发语言·前端·flutter·dart
—Qeyser3 小时前
Flutter Container 容器组件完全指南
flutter
—Qeyser3 小时前
Flutter 生命周期完全指南:从出生到死亡的全过程
前端·javascript·flutter
HH思️️无邪3 小时前
Flutter 项目 -从 0 到 1 实现 iOS WatchApp
flutter·ios
—Qeyser3 小时前
Flutter StatelessWidget 完全指南:构建高效的静态界面
前端·flutter