Flutter: Stream has already been listened to

8,926

It is easy, take a look to BehaviorSubject class from RxDart library.

BehaviorSubject is, by default, a broadcast (aka hot) controller, in order to fulfill the Rx Subject contract. This means the Subject's stream can be listened to multiple times.

So, just change line

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

to

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

and delete all

.asBroadcastStream()

That's it!

In official documentation it is not recommended to use asBroadcastStream()

A more dangerous way of creating a stream controller is to view a single-subscription controller through asBroadcastStream(). Invoking asBroadcastStream basically tells the single-subscription stream that the user wants to take over the lifetime management of the stream. In combination with cancelOnError subscribers, this can easily lead to single-stream subscriptions that are never closed and thus leak memory or resources.

Share:
8,926
Jonas
Author by

Jonas

Updated on December 08, 2022

Comments

  • Jonas
    Jonas over 1 year

    I'm using BLoC to load my Preset Objects from Firestore. This is my Bloc Model:

    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);
      }
    }
    

    Then I display the List like this:

    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');
              }
            }
          )
        );
      }
    }
    

    The problem is, I show the the StatisticsPage in a Tabbar, so it will be build muliple times when I switch tabs and go back to it. On the first visit it works but when I switch tabs and go back to it, the widget get rebuild and I get the error: Bad state: Stream has already been listened to.. I tried to declare the getPresets Stream as a BroadcastStream as you can see in StatisitcsBloc but that doesn't work.

    Also as a secoundary question: Is there a better way to transform Stream<QuerySnapshot> that I get from Firestore to Stream<List<Presets>>?

  • Valentina Konyukhova
    Valentina Konyukhova almost 5 years
    It helps, but you forget to mention that this answer requires RxDart library
  • Hamza Abdullah
    Hamza Abdullah almost 3 years
    this answer save my day thank you