Flutter:Stream已经被监听过了。

11

我正在使用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流声明为BroadcastStream,如您在StatisitcsBloc中看到的,但这行不通。

另外,作为一个辅助问题:有没有更好的方法将我从Firestore获取的Stream<QuerySnapshot>转换为Stream<List<Presets>>


你能在创建StatisticsBloc的地方添加吗?同时附上完整的错误和堆栈跟踪。 - Lucas
6
这个问题有解决了吗?Flutter:Bad state: Stream has already been listened to - BINAY THAPA MAGAR
1个回答

27

很简单,看一下RxDart库中的BehaviorSubject类

默认情况下,BehaviorSubject是广播(也称为热)控制器,以满足Rx主题合同。这意味着可以多次监听Subject的流。

所以,只需更改行

StreamController<List<Preset>> _presetsController = new StreamController();

to

StreamController<List<Preset>> _presetsController = new BehaviorSubject();

并且删除所有{{内容}}。

.asBroadcastStream()

就是这样!

在官方文档中,不建议使用asBroadcastStream()函数

创建流控制器的一种更危险的方法是通过asBroadcastStream()查看单订阅控制器。调用asBroadcastStream()基本上告诉单订阅流,用户想要接管流的生命周期管理。与cancelOnError订阅者结合使用,这很容易导致永远不关闭的单流订阅,从而泄漏内存或资源。


5
这很有帮助,但您忘了提到这个答案需要使用RxDart库。 - Valentina Konyukhova
这个答案救了我的一天,谢谢。 - Hamza Abdullah

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接