我正在使用BLoC从Firestore加载我的预设对象。这是我的Bloc模型:
class StatisticsBloc extends BlocBase { List<Preset> _presets; StreamController<List<Preset>> _presetsController = new StreamController(); Stream<List<Preset>> get getPresets => _presetsController.stream.asBroadcastStream(); StatisticsBloc() { print('init Statistics Bloc'); _presets = []; Firestore.instance.collection('Presets').snapshots().asBroadcastStream().listen(_onPresetsLoaded); } @override void dispose() { print('Disposed Statistics Bloc'); _presetsController.close(); } void _onPresetsLoaded(QuerySnapshot data) { _presets = []; data.documents.forEach((DocumentSnapshot snap) { Preset preset = Preset.fromDoc(snap); _presets.add(preset); }); _presetsController.sink.add(_presets); } }
然后我显示如下列表:
class StatisticsPage extends StatelessWidget { StatisticsPage() { print('Created StatisticsPage'); } @override Widget build(BuildContext context) { final StatisticsBloc statisticsBloc = BlocProvider.of<StatisticsBloc>(context); final List<Preset> _ = []; print(statisticsBloc.getPresets.isBroadcast); return Scaffold( appBar: AppBar( title: Text('Statistics'), ), body: StreamBuilder( stream: statisticsBloc.getPresets, initialData: _, builder: (BuildContext context, AsyncSnapshot<List<Preset>> snapshot) { if (snapshot.hasData) { return ListView( children: snapshot.data.map((Preset preset) { print(preset.name); return new ListTile( title: new Text(preset.name), subtitle: new Text(preset.id), ); }).toList(), ); } else { Text('No Data'); print('No Data'); } } ) ); } }
问题是,我StatisticsPage在标签栏中显示了,因此当我切换标签并返回到该标签栏时,这将是多次构建的时间。第一次访问时它可以工作,但是当我切换选项卡并返回到它时,小部件将重新构建,并且出现错误:Bad state: Stream has already been listened to.。getPresets如您所见,我试图将Stream 声明为BroadcastStream,StatisitcsBloc但这不起作用。
StatisticsPage
Bad state: Stream has already been listened to.
getPresets
StatisitcsBloc
还有一个次要问题:是否有更好的方法将Stream<QuerySnapshot>我从Firestore转换为Stream<List<Presets>>?
Stream<QuerySnapshot>
Stream<List<Presets>>
这是很容易,看看到BehaviorSubject类从RxDart库。
默认情况下,BehaviorSubject是广播(又称热播)控制器,以履行Rx Subject合同。这意味着可以多次收听主题的流。
所以,只要换线
StreamController<List<Preset>> _presetsController = new StreamController();
至
StreamController<List<Preset>> _presetsController = new BehaviorSubject();
并全部删除
.asBroadcastStream()
而已!
在官方文档中,不建议使用asBroadcastStream()
创建流控制器的一种更危险的方法是通过asBroadcastStream()查看单个订阅控制器。调用asBroadcastStream基本上可以告诉单订阅流用户想要接管该流的生命周期管理。与cancelOnError订阅服务器结合使用,很容易导致单流订阅永远不会关闭,从而导致内存或资源泄漏。