StreamBuilder is not rebuilding on new event in stream

4,225

Solution 1

The issue was I was instantiaing my BLOC incorrectly(second one) and working on a second parallel stream and therefore not the one passed to StreamBuilder.

Solution 2

Your bloc needs to be a type of Stream. The same stream type as your StreamBuilder. For example your bloc needs to be a Stream<Alarm>. Otherwise stream: widget.bloc.alarmController.stream, will only be called once and won't act as an asynchronous stream of data.

Your Streambuilder needs to check for connection states

Widget build(BuildContext context) {
    print("rebuilding..."); // as of now this gets called only on view initialization and never again - i.e. not on new events going through alarmController.stream

    return StreamBuilder<Alarm>(
        stream: widget.bloc.alarmController.stream,
        initialData: Alarm(''),
        builder: (BuildContext context, AsyncSnapshot<Alarm> snapshot) {
          if (snapshot.hasError) return new Text('Error: ${snapshot.error}');
      switch (snapshot.connectionState) {
        case ConnectionState.waiting:
          return Text('Loading...'); 
        default: 
          if (!snapshot.hasData) {
             return Center(
                child: Text(StringLiterals.NO_ALARM_DATA_MSG))
             );
          }

         return Switch(
              activeColor: Colors.red,
              value: snapshot.data.status == 'Started',
              onChanged: (bool _value) {
                _newAlarmValue = _value;
                _askAlarmConfirmation();
              }));
      }

Here are the other types of connection states you can check for:

async.dart

    enum ConnectionState {
  /// Not currently connected to any asynchronous computation.
  ///
  /// For example, a [FutureBuilder] whose [FutureBuilder.future] is null.
  none,

  /// Connected to an asynchronous computation and awaiting interaction.
  waiting,

  /// Connected to an active asynchronous computation.
  ///
  /// For example, a [Stream] that has returned at least one value, but is not
  /// yet done.
  active,

  /// Connected to a terminated asynchronous computation.
  done,
}
Share:
4,225
Alan Kałuża
Author by

Alan Kałuża

Updated on December 11, 2022

Comments

  • Alan Kałuża
    Alan Kałuża over 1 year

    My StreamBuilder in view:

    Widget build(BuildContext context) {
        print("rebuilding..."); // as of now this gets called only on view initialization and never again - i.e. not on new events going through alarmController.stream
    
        return StreamBuilder(
            stream: widget.bloc.alarmController.stream,
            initialData: Alarm(''),
            builder: (BuildContext context, AsyncSnapshot<Alarm> snapshot) {
              if (!snapshot.hasData) {
                return Center(
                  child: Text(StringLiterals.NO_ALARM_DATA_MSG))
                );
              }
    
              return Switch(
                      activeColor: Colors.red,
                      value: snapshot.data.status == 'Started',
                      onChanged: (bool _value) {
                        _newAlarmValue = _value;
                        _askAlarmConfirmation();
                      }));
            });
      }
    

    meat of my bloc:

    AlarmBloc(this.Api) {
        getAlarm();
    }
      getAlarm() async {
        Alarm response = await Api.getAlarmStatus();
        alarmController.sink.add(response); // here im adding new event, therefore streambuilder should rebuild, right?
    }
    

    And lastly the code I call to initiate new event(in this case it's firebase message):

    if(_message.notification.body.contains("Alarm") && IS_LOGGED_IN == true) {
       alarmBloc.getAlarm();
     }
    

    So the problem is StreamBuilder not rebuilding whenever new event passes through alarmController.stream. What could be the reason?

    • Mazin Ibrahim
      Mazin Ibrahim almost 5 years
      Try testing by adding your print statement inside StreamBuilder. AFAIK it doesn't force the entire widget to rebuild.
    • Marcos Boaventura
      Marcos Boaventura almost 5 years
      I am ssuming that you're using rxDart so tell me something what kind of stream controller are you using? PublishSubject or BehaviorSubject?
    • Alan Kałuża
      Alan Kałuża almost 5 years
      It's BehaviourSubject @MarcosBoaventura
  • Alan Kałuża
    Alan Kałuża almost 5 years
    Unfortunately, this isn't the case. The connection is not being terminated and even with StreamBuilder type given it's still not rebuilding the widget on new event being added to sink...
  • Val
    Val almost 5 years
    Streams can be tricky. There are more options we can check out for you. We would need to reconfigure the calling stream in place of sending the 'response' back through the controller. It looks like the getAlarm is only getting call on the build and isn't streaming data back asynchronously. First can you tell me what type of response Type you are expecting from getAlarm?