基于InitialState未就绪的BLoC数据构建Widget的问题及代码解析
问题描述
我开发简易Flutter应用时遇到场景:部分Widget依赖InitialState阶段未就绪的计算后模型数据,原本打算用基础初始数据加载后再触发重建。但有两个疑问:
- 向还没渲染的BlocBuilder发送状态会不会报错?
- BLoC是不是只能通过初始状态首次构建Widget树?
最后我在目标Widget的父组件build方法首行触发了LoadGame事件,通过BlocBuilder根据GameLoaded、GameUpdated状态返回GolTable,否则返回占位组件,代码能正常运行,但我搞不懂为什么没出现预期的错误,想请人解释这段代码的运行逻辑。
代码如下:
class GamePage extends StatelessWidget { GamePage({super.key}); @override Widget build(BuildContext context) { BlocProvider.of<GolFieldBloc>(context).add(LoadGame()); return Scaffold( appBar: AppBar( leading: IconButton( icon: Icon(Icons.arrow_back), onPressed: () { Navigator.pop(context); }, ), title: const Center( child: Hero(tag: "dikk", child: Text('setup')), ), backgroundColor: Color.fromARGB(200, 200, 200, 200), ), body: Container( alignment: Alignment.center, color: Color.fromARGB(100, 100, 100, 100), child: Body(), ), floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, floatingActionButton: FloatingActionButton( onPressed: () { BlocProvider.of<GolFieldBloc>(context).add(StepInSimulation()); }, child: Icon(Icons.skip_next), ), ); } } class Body extends StatelessWidget { const Body({super.key}); @override Widget build(BuildContext context) { return BlocBuilder<GolFieldBloc, GolFieldState>( builder: (context, state) { if (state is GameLoaded) { return GolTable(grid: state.grid); } if (state is GameUpdated) { return GolTable(grid: state.grid); } else { return Placeholder(); } }, ); } }
运行逻辑解析
1. Bloc实例与BlocBuilder是独立的
你能调用BlocProvider.of<GolFieldBloc>(context),说明GolFieldBloc已经在当前/上层上下文被BlocProvider初始化并提供了——Bloc实例的存在和BlocBuilder是否渲染完全无关。你疑惑的"未存在的BlocBuilder"只是监听状态的Widget,而发送事件是给Bloc实例的,所以根本不会触发"向不存在的对象发送事件"的错误。
2. 首次构建的状态流转流程
- 当
GamePage第一次执行build时,立刻向Bloc发送LoadGame事件,此时Bloc从初始状态(比如GameInitial)开始异步处理事件; - 第一次渲染
Body中的BlocBuilder时,Bloc还处于初始状态,所以会返回Placeholder占位组件; - 当Bloc处理完
LoadGame事件、发出GameLoaded状态后,BlocBuilder会自动监听到状态变化,触发自身重建,此时就会返回依赖模型数据的GolTable。
3. BLoC并非只能用初始状态构建Widget树
BlocBuilder的核心作用就是监听Bloc的状态变化,每次状态更新都会重新执行builder方法返回新的Widget。初始状态只是第一次渲染的默认依据,后续只要Bloc状态更新(比如GameUpdated),BlocBuilder就会自动重建对应的Widget分支,完全不需要手动触发整个Widget树的重建。
4. 关于build中触发事件的注意点
虽然当前代码能运行,但不推荐在build方法里直接调用add——因为build可能被多次触发(比如屏幕旋转、父组件重建),会导致重复发送LoadGame事件。可以改成:
- 将
GamePage改为StatefulWidget,在initState中发送一次事件; - 在Bloc内部添加逻辑,判断当前状态如果已经是
GameLoaded就忽略重复的LoadGame事件。
内容的提问来源于stack exchange,提问作者Egy Ketto

