Flutter Bloc模式问题:跳转Settings页面后无法获取初始化数据
问题分析与解决方案
我一眼就看到两个关键问题导致了你遇到的现象,咱们一个个说:
1. Broadcast 流不会缓存历史事件(核心原因)
你的StateBloc使用了StreamController<List>.broadcast(),这种广播流的特性是:只有在订阅发生之后发送的事件,才会被订阅者接收到。
- 首页的
StreamBuilder在App启动时就完成了订阅,所以能顺利拿到Bloc构造函数里发送的初始数据['cny','aud','usd']; - 而Settings页面是通过
Navigator.push跳转过去的,当它的StreamBuilder订阅流时,构造函数里的初始事件早就发送完毕了,自然拿不到数据,显示null; - 至于你测试添加新数据能正常显示,是因为添加操作是在订阅之后执行的,新事件能被实时接收。
2. Settings页面多余的MaterialApp
你的Settings页面里嵌套了MaterialApp,这会创建一个全新的独立导航上下文。虽然目前看起来没影响Bloc的获取,但这完全没必要,还可能导致后续路由、主题继承等问题,必须移除。
推荐解决方案:用BehaviorSubject缓存最新值
最优雅的解决方式是使用rxdart库的BehaviorSubject,它会自动缓存最新的事件,新订阅者一订阅就能拿到当前的最新值(包括初始值)。
步骤1:添加rxdart依赖
在pubspec.yaml里添加:
dependencies: flutter: sdk: flutter rxdart: ^0.27.7 # 可以替换为最新版本
步骤2:改造Bloc代码
import 'package:rxdart/rxdart.dart'; class StateBloc implements BlocBase { late final BehaviorSubject<List<String>> _selectedCurrencies; // 对外暴露的流,自动缓存最新值 Stream<List<String>> get getSelectedCurrencies => _selectedCurrencies.stream; StreamSink<List<String>> get modifySelectedCurrencies => _selectedCurrencies.sink; StateBloc() { // 直接用seeded初始化带有初始值的BehaviorSubject _selectedCurrencies = BehaviorSubject.seeded(['cny','aud','usd']); } // 必须实现dispose,避免内存泄漏 @override void dispose() { _selectedCurrencies.close(); } }
步骤3:修复Settings页面
移除多余的MaterialApp,并确保StreamBuilder正常工作:
class Settings extends StatelessWidget { @override Widget build(BuildContext context) { final StateBloc appBloc = BlocProvider.of<StateBloc>(context); return Scaffold( body: ListView( children: <Widget>[ StreamBuilder( stream: appBloc.getSelectedCurrencies, builder: (context, selectedCurrenciesSnap) { print('alllll --> ${selectedCurrenciesSnap.data}'); // 记得返回一个Widget,比如显示数据的文本 return Text(selectedCurrenciesSnap.data?.join(', ') ?? 'No data'); } ) ], ), ); } }
备选方案:不用rxdart,手动处理初始值
如果你不想引入额外依赖,可以给StreamBuilder设置initialData,直接使用Bloc里的初始值:
改造Bloc
class StateBloc implements BlocBase { List<String> selectedCurrencies = ['cny','aud','usd']; late final StreamController<List<String>> _selectedCurrencies; Stream<List<String>> get getSelectedCurrencies => _selectedCurrencies.stream; StreamSink<List<String>> get modifySelectedCurrencies => _selectedCurrencies.sink; StateBloc() { _selectedCurrencies = StreamController<List<String>>.broadcast(); // 初始值存在selectedCurrencies变量里 } // 更新数据时同步变量和流 void updateCurrencies(List<String> newList) { selectedCurrencies = newList; modifySelectedCurrencies.add(newList); } @override void dispose() { _selectedCurrencies.close(); } }
改造Settings页面的StreamBuilder
StreamBuilder( stream: appBloc.getSelectedCurrencies, initialData: appBloc.selectedCurrencies, // 直接用Bloc里的初始值 builder: (context, selectedCurrenciesSnap) { print('alllll --> ${selectedCurrenciesSnap.data}'); return Text(selectedCurrenciesSnap.data?.join(', ') ?? 'No data'); } )
不过这种方式需要手动维护selectedCurrencies变量和流的同步,不如BehaviorSubject省心,更推荐第一种方案。
内容的提问来源于stack exchange,提问作者Panda Power
相关产品推荐
相关产品推荐

