Bloc: Bad state: Cannot add new events after calling close after the bloc is disposed.

Created on 1 Mar 2019  路  23Comments  路  Source: felangel/bloc

I got this error while trying a scenario:

  1. I need to fetch some data from the API.
  2. To simulate the fetching delay time, i use await Future.delayed(Duration(seconds: 2)); in the mapEventToState bloc. Then i yield some state.
  3. I dispose the bloc when the page is popped.
  4. When i press back (dispose) the page before the fetching finished (under 2 seconds), the error below happened. The apps did not crashed though.

Seems like it is similar with #52

E/flutter (14028): [ERROR:flutter/lib/ui/ui_dart_state.cc(148)] Unhandled Exception: Bad state: Cannot add new events after calling close
E/flutter (14028): #0      _BroadcastStreamController.add (dart:async/broadcast_stream_controller.dart:249:24)
E/flutter (14028): #1      Subject._add (package:rxdart/src/subjects/subject.dart:124:16)
E/flutter (14028): #2      Subject.add (package:rxdart/src/subjects/subject.dart:118:5)
E/flutter (14028): #3      Bloc._bindStateSubject.<anonymous closure> (package:bloc/src/bloc.dart:86:23)
E/flutter (14028): #4      Stream.forEach.<anonymous closure>.<anonymous closure> (dart:async/stream.dart:814:45)
E/flutter (14028): #5      _runUserCode (dart:async/stream_pipe.dart:11:23)
E/flutter (14028): #6      Stream.forEach.<anonymous closure> (dart:async/stream.dart:814:11)
E/flutter (14028): #7      _rootRunUnary (dart:async/zone.dart:1132:38)
E/flutter (14028): #8      _CustomZone.runUnary (dart:async/zone.dart:1029:19)
E/flutter (14028): #9      _CustomZone.runUnaryGuarded (dart:async/zone.dart:931:7)
E/flutter (14028): #10     _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:336:11)
E/flutter (14028): #11     _BufferingStreamSubscription._add (dart:async/stream_impl.dart:263:7)
E/flutter (14028): #12     _SyncBroadcastStreamController._sendData (dart:async/broadcast_stream_controller.dart:375:20)
E/flutter (14028): #13     _BroadcastStreamController._add (dart:async/broadcast_stream_controller.dart:287:5)
E/flutter (14028): #14     _rootRunUnary (dart:async/zone.dart:1132:38)
E/flutter (14028): #15     _CustomZone.runUnary (dart:async/zone.dart:1029:19)
E/flutter (14028): #16     _CustomZone.runUnaryGuarded (dart:async/zone.dart:931:7)
E/flutter (14028): #17     _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:336:11)
E/flutter (14028): #18     _BufferingStreamSubscription._add (dart:async/stream_impl.dart:263:7)
E/flutter (14028): #19     _ForwardingStreamSubscription._add (dart:async/stream_pipe.dart:132:11)
E/flutter (14028): #20     _ForwardingStream._handleData (dart:async/stream_pipe.dart:98:10)
E/flutter (14028): #21     _ForwardingStreamSubscription._handleData (dart:async/stream_pipe.dart:164:13)
E/flutter (14028): #22     _rootRunUnary (dart:async/zone.dart:1132:38)
E/flutter (14028): #23     _CustomZone.runUnary (dart:async/zone.dart:1029:19)
E/flutter (14028): #24     _CustomZone.runUnaryGuarded (dart:async/zone.dart:931:7)
E/flutter (14028): #25     _BufferingStreamSubscription._sendData (dart:async/stream_impl.dart:336:11)
E/flutter (14028): #26     _DelayedData.perform (dart:async/stream_impl.dart:591:14)
E/flutter (14028): #27     _StreamImplEvents.handleNext (dart:async/stream_impl.dart:707:11)
E/flutter (14028): #28     _PendingEvents.schedule.<anonymous closure> (dart:async/stream_impl.dart:667:7)
E/flutter (14028): #29     _rootRun (dart:async/zone.dart:1120:38)
E/flutter (14028): #30     _CustomZone.run (dart:async/zone.dart:1021:19)
E/flutter (14028): #31     _CustomZone.runGuarded (dart:async/zone.dart:923:7)
E/flutter (14028): #32     _CustomZone.bindCallbackGuarded.<anonymous closure> (dart:async/zone.dart:963:23)
E/flutter (14028): #33     _rootRun (dart:async/zone.dart:1124:13)
E/flutter (14028): #34     _CustomZone.run (dart:async/zone.dart:1021:19)
E/flutter (14028): #35     _CustomZone.runGuarded (dart:async/zone.dart:923:7)
E/flutter (14028): #36     _CustomZone.bindCallbackGuarded.<anonymous closure> (dart:async/zone.dart:963:23)
E/flutter (14028): #37     _microtaskLoop (dart:async/schedule_microtask.dart:41:21)
E/flutter (14028): #38     _startMicrotaskLoop (dart:async/schedule_microtask.dart:50:5)
E/flutter (14028): 
question

Most helpful comment

Hello, Felangel, how are you?
Suggest that in a next version, make an adjustment in the class Bolc.
In the dispose method, I did this way and the error stopped being displayed.

  /// Closes the [Event] and [State] [Stream]s.
  @mustCallSuper
  void dispose() async{
    await _eventSubject.drain();
    _eventSubject.close();
    await _stateSubject.drain();
    _stateSubject.close();
  }

All 23 comments

@haqqi thanks for opening an issue!

I would recommend creating and disposing the bloc one level above where you are doing it now to resolve this. The bloc should be disposed only when you are certain it is no longer being used.

Let me know if that helps! 馃憤

Closing this for now. Feel free to comment if that didn鈥檛 help and I will reopen the issue.

@haqqi thanks for opening an issue!

I would recommend creating and disposing the bloc one level above where you are doing it now to resolve this. The bloc should be disposed only when you are certain it is no longer being used.

Let me know if that helps! 馃憤

Actually i use the bloc at highest level, which is just below body tag of Scaffold. I could not put the bloc in the MaterialApp, because the bloc is supposed to be used in just one page/route only.

@haqqi can you please share a sample application which illustrates the problem you're having?

Hello, Felangel, how are you?
Suggest that in a next version, make an adjustment in the class Bolc.
In the dispose method, I did this way and the error stopped being displayed.

  /// Closes the [Event] and [State] [Stream]s.
  @mustCallSuper
  void dispose() async{
    await _eventSubject.drain();
    _eventSubject.close();
    await _stateSubject.drain();
    _stateSubject.close();
  }

@glourencosilva thanks for the suggestion but I don't think we want to hide the error. The error is valuable because it indicates to the developer that they disposed the bloc while it is still being used in other parts of the application.

@felangel I like to see this error in console, because of this I know that streamSubscription that I created in the bloc are trying to dispatch something, and I can fix that behavior, without that i never know that this subscription are not closed;
thanks

@basketball-ico you will see this error in the console if you override onError and print errors to the console.

You can either override onError at the bloc level (for a specific bloc) or globally in BlocDelegate.

Hope that helps 馃憤

Hello, Felangel, how are you?
Suggest that in a next version, make an adjustment in the class Bolc.
In the dispose method, I did this way and the error stopped being displayed.

  /// Closes the [Event] and [State] [Stream]s.
  @mustCallSuper
  void dispose() async{
    await _eventSubject.drain();
    _eventSubject.close();
    await _stateSubject.drain();
    _stateSubject.close();
  }

Holly molly! That totally saved my ass after 2 days of research! I'm not even using this library, just plain small Bloc class which has one dispose() method and list of subscriptions. It was working just fine, but then I've noticed that sometimes web version would crash randomly. After this fix it works like charm, thank you very much!

Hi @felangel, I'm having similar issuer,Cannot add new events after calling close

What's the best way to avoid this error.

In my case, I have 3 screens
Maps View,
List View
and Filters

Inside maps view is where I dispatch the event and listen to events changes in other screens.

When landing on the maps view, I dispatch the even, to make a call to the API to get data.

The same data I populate for both Maps and List view.

and the filter screen I just dispatch the event to filter the data, and the same filtered data should be equal in the maps and list view.

But the issue I'm facing, when I make a filter from the Maps View, and go to List View, try to filter again,
it says Cannot add new events after calling close, which means I only can dispatch the filter event from one screen.

Can you please help to understand nicely how to avoid this kind of error

Hi @jjoao07 make sure you aren't manually disposing/closing blocs (leave it to be handled by BlocProvider instead) and you should be good to go.

Hi @felangel I am using flutter_bloc: ^3.1.0, I hope in latest version of the bloc we don't need to dispose the bloc and in my project i am not disposing any of my bloc, so I am having three tab bars each one has seperate bloc whenever i am opening new tabs doing some operations then back to another tab when i click something getting error. " Bad state: Cannot add new events after calling close"

thanks in advance

@ThalapathySiva can you please provide a link to a sample app which illustrates the problem you're having? You shouldn't need to manually dispose your blocs but the error you're getting means that somewhere in your app something is trying to add events to the bloc after the bloc has been disposed. Usually, the solution is to raise your BlocProvider higher in the widget tree.

@ThalapathySiva i'm having the same issue, with the same scenario.

Hello @felangel , just upgrade bloc version to 4.0.0 (3.0.0 before), and then randomly get error:

I/flutter (25994): Another exception was thrown: Unhandled error Bad state: Cannot add new events after calling close occurred in bloc Instance of 'ArticleYoutubeBloc'. E/flutter (25994): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: 'package:flutter/src/widgets/scroll_activity.dart': Failed assertion: line 599 pos 15: 'to != null': is not true. E/flutter (25994): #0 _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:42:39) E/flutter (25994): #1 _AssertionError._throwNew (dart:core-patch/errors_patch.dart:38:5) E/flutter (25994): #2 new DrivenScrollActivity (package:flutter/src/widgets/scroll_activity.dart:599:15) E/flutter (25994): #3 ScrollPositionWithSingleContext.animateTo (package:flutter/src/widgets/scroll_position_with_single_context.dart:183:43) E/flutter (25994): #4 ScrollController.animateTo (package:flutter/src/widgets/scroll_controller.dart:153:37) E/flutter (25994): #5 _MarqueeWidgetState.scroll (package:finoo/ui/marquee_widget.dart:49:28) E/flutter (25994): <asynchronous suspension> E/flutter (25994): #6 SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1102:15) E/flutter (25994): #7 SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1049:9) E/flutter (25994): #8 SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:957:5) E/flutter (25994): #9 _rootRun (dart:async/zone.dart:1126:13) E/flutter (25994): #10 _CustomZone.run (dart:async/zone.dart:1023:19) E/flutter (25994): #11 _CustomZone.runGuarded (dart:async/zone.dart:925:7) E/flutter (25994): #12 _invoke (dart:ui/hooks.dart:259:10) E/flutter (25994): #13 _drawFrame (dart:ui/hooks.dart:217:3)

and after this error, the red error box will take over the app.

Hi @bintangursiboro 馃憢
Are you able to share a link to a sample which reproduces the issue so I can run/debug it locally? Thanks 馃憤

Currently cannot share any link for you, but here's a thing,
So I have bottom navigation -> Home, Settings, etc.
so I use bloc provider for temp saving the home state (fetching data from server at the first time when app launch), and then after I tap setting navigation, and back tap home, and bloc is re fetching data from server again. In previous version, bloc is fetching data just once when launching the app.
Hope this detail is helping. Thank you

Can anyone tell me, how to cancel the previous event and add a new event?.

@satishsoni777 have you tried overriding transformEvents to use switchMap?

No, I have not tried. could you please share more details about it.?

Hi @satishsoni777 馃憢

Please have a look at https://github.com/felangel/bloc/issues/1107#issuecomment-621176290 for an example. 馃憤

@glourencosilva is the solution... TKS

@felangel Hi Dear Felix, i've using your library since a long time ago, but i've a question, for example i use a bloc to register new users and if i call _registerBloc.close() on dispose(), i can't register more users, if the user calls again this screen because close events added will not processed, and i've the same trouble with another blocs, the user maybe don't use immediately聽but later can be use a bloc again, if i not call dispose(), the app work well, but it is a memory fail? what's happens if I never call close() on the blocs? Maybe i've 8 on my app.

Thank for your help, nice day.

Was this page helpful?
0 / 5 - 0 ratings