Giter Club home page Giter Club logo

Comments (75)

felangel avatar felangel commented on May 18, 2024 17

@rrifafauzikomara you need to remove final bloc = MealsDetailBloc(); from your detail bloc and instead in your widget do something like:

final bloc = MealsDetailBloc();

@override
void initState() {
  super.initState();
  bloc.fetchDetailMeals(widget.idMeal);
}
 
@override
void dispose() {
  bloc.dispose();
  super.dispose();
}

This way your widget is creating and disposing the bloc. Before, the widget only disposes the bloc but it is never re-created when the widget is re-created. Hope that helps 👍

from bloc.

anderscheow avatar anderscheow commented on May 18, 2024 6

I'm getting this error message probably due to some how the stream is closed while waiting some API to done, and once the API is done, it will dispatch an event and then causes this error

from bloc.

felangel avatar felangel commented on May 18, 2024 5

Are your blocs being created by a statefulwidget? They should be part of a statefulwidget so that they aren’t recreated on rebuilds.

from bloc.

enricobenedos avatar enricobenedos commented on May 18, 2024 5

Oh! It's my mistake! I was sure I'm writing on Flutter framework repository 😅. Really sorry guys. Anyway I've updated my project with the dispose in the main.dart. Thank you for all the provided support to improve my code

from bloc.

felangel avatar felangel commented on May 18, 2024 3

No problem!

This issue happens if the Bloc is disposed before an event is dispatched. I would recommend initializing and disposing your blocs one level higher than they currently are.

You can confirm that this is the issue by commenting out all the places where you call bloc.dispose() and verifying that you are no longer seeing the error. Let me know if that helps! 👍

from bloc.

felangel avatar felangel commented on May 18, 2024 3

@JoseGeorges8 one last PR to fix the equatable usage JoseGeorges8/bloc_issue#2

from bloc.

JoseGeorges8 avatar JoseGeorges8 commented on May 18, 2024 3

Hey Felix! just wanted to let you know that your solution worked!

Thanks for the help

from bloc.

MohsinN avatar MohsinN commented on May 18, 2024 2

Hi,

Firstly, really great work with the library!

I am facing the same error but the reason i believe is not the framework but flutter itself. After Material page route, all the pages on the stack are re built. Issue for reference #14124 , #11655 . Dont know the ETA for the fix.

Streams are closed properly on navigation when on onDispose is called. But when pages on stack are rebuilt than this issue is produced. If you can add check to detect dispose is called before dispatching the event than this error can be avoided. I tried adding boolean checks but that did not help.

Please let me know if i missed something out.

from bloc.

JoseGeorges8 avatar JoseGeorges8 commented on May 18, 2024 2

Quick update! I did some changes to the code to mimic more my project and the issue appeared!

I added a new event and a new state, tab1 dispatches a different event than tab2 and 3 and expects a different state than 2 and 3. Now is visible that when tab1 gets initialized, the state being transitioning is not the correct?

Take a look and let me know thanks!

from bloc.

felangel avatar felangel commented on May 18, 2024 2

@JoseGeorges8 ah this is because initState can be called in any order so in this case you start off with Tab1 being active (initState called) so the fetchInfo event is dispatched. Then when you navigate to Tab3, the initState for Tab3 is called first so we dispatch the second fetchInfo but after that the initState for Tab2 is called which changes the shared bloc state to the second state and since initState is only called once when you navigate back to Tab1 initState is not called and the state does not change so you are left in the state for Tab2. Does that make sense?

from bloc.

JoseGeorges8 avatar JoseGeorges8 commented on May 18, 2024 2

Looks great! I'll test it inside my app tomorrow and I'll let you now

Thank you Felix!

from bloc.

craiglabenz avatar craiglabenz commented on May 18, 2024 2

@enricobenedos From a high level, you either need to not dispose() your bloc (make it global and inherit it from the BlocProvider widget), or recreate it when returning to a screen.

from bloc.

craiglabenz avatar craiglabenz commented on May 18, 2024 2

@enricobenedos I don't believe these ideas are at odds if you create an app-wide bloc in your root widget 😄

from bloc.

felangel avatar felangel commented on May 18, 2024 2

@nerder hey as a rule of thumb whoever creates the bloc should close the bloc. In general I would recommend leaving it up to BlocProvider to handle everything for you (so you don't need StatefulWidgets) but if you need to create a StatefulWidget and want to manually create an instance of the bloc then it should look something like:

class _MyWidgetState extends State<MyWidget> {
  final _bloc = MyBloc();

  @override
  void initState() {
    super.initState();
    _bloc.add(MyEvent());
  }

  @override
  Widget build(BuildContext context) { ... }

  @override
  void dispose() {
    _bloc.close();
    super.dispose();
  }
}

Hope that helps 👍

from bloc.

anderscheow avatar anderscheow commented on May 18, 2024 1

Sorry, I can't share my code for now. But I will try to explain.

I did not call any bloc's dispose() method. I got a TabBar with 3 tabs, each tab has it's own bloc. When I push to a new page from one of the tabs, sometimes the error triggered. But I've noticed that every time I pop from the new page back to the tab screen, the tab screen some how get rebuild.

from bloc.

anderscheow avatar anderscheow commented on May 18, 2024 1

I will try to construct an example to determine whether the issue will occur. If it doesn't, it might be indicate problem on my code otherwise I will give you the example

from bloc.

felangel avatar felangel commented on May 18, 2024 1

Yup, you need to override dispose in the statefulwidget and call the bloc’s dispose.

from bloc.

anderscheow avatar anderscheow commented on May 18, 2024 1

Alright thanks! I'll give it a shot.

from bloc.

JoseGeorges8 avatar JoseGeorges8 commented on May 18, 2024 1

I'll try right now thanks!

from bloc.

JoseGeorges8 avatar JoseGeorges8 commented on May 18, 2024 1

ok I'll try to replicate it and get back to you!

from bloc.

JoseGeorges8 avatar JoseGeorges8 commented on May 18, 2024 1

Hey @felangel , sorry for the wait!

So I did a basic example of the pages where I'm implementing the bloc:

https://github.com/JoseGeorges8/bloc_issue

unfortunately, the bug does not happen in here! The difference right now between this and my project is layout as well as mine has 2 blocs right now, So I'll try to add the changes and see if the bug appears!

from bloc.

felangel avatar felangel commented on May 18, 2024 1

@JoseGeorges8 no worries! Keep me posted 👍

from bloc.

felangel avatar felangel commented on May 18, 2024 1

Awesome! I’ll have a look in the next hour 😄

from bloc.

felangel avatar felangel commented on May 18, 2024 1

Check out JoseGeorges8/bloc_issue#1

I'd recommend having a bloc for each tab state along with AutomaticKeepAliveClientMixin to reduce the number of rebuilds.

Let me know if that helps!

from bloc.

jonataslaw avatar jonataslaw commented on May 18, 2024 1

Oi,

Em primeiro lugar, um ótimo trabalho com a biblioteca!

Estou enfrentando o mesmo erro, mas a razão pela qual acredito não é a estrutura, mas a vibração em si. Após a rota da página Material, todas as páginas da pilha são recriadas. Problema para referência # 14124 , # 11655 . Não sei o ETA para a correção.

Os fluxos são fechados corretamente na navegação quando onDispose é chamado. Mas quando as páginas na pilha são reconstruídas, esse problema é produzido. Se você pode adicionar a verificação para detectar a eliminação é chamada antes de despachar o evento, esse erro pode ser evitado. Tentei adicionar cheques booleanos, mas isso não ajudou.

Por favor, deixe-me saber se eu perdi alguma coisa.

flutter/flutter#44731

Hi,

Firstly, really great work with the library!

I am facing the same error but the reason i believe is not the framework but flutter itself. After Material page route, all the pages on the stack are re built. Issue for reference #14124 , #11655 . Dont know the ETA for the fix.

Streams are closed properly on navigation when on onDispose is called. But when pages on stack are rebuilt than this issue is produced. If you can add check to detect dispose is called before dispatching the event than this error can be avoided. I tried adding boolean checks but that did not help.

Please let me know if i missed something out.

I offered a pull to fix the problem with navigation rebuilding everything below materialApp.
flutter/flutter#44731

Alternatively I created a navigation package which besides not having any route reconstruction bugs, can be used with BLoC as it does not require context to navigate.

https://pub.dev/packages/get

from bloc.

felangel avatar felangel commented on May 18, 2024 1

Hey @jjoao07 👋
Sorry to hear that! I'm happy to help you walk through your code and fix these issues. If you haven't already please join the bloc discord server and message me when you have some time. We can set up a live code share and work through the issues you're facing 👍

from bloc.

felangel avatar felangel commented on May 18, 2024

I don’t think this stack trace is coming from the bloc library but in any case the problem is you are closing your stream and later trying to add to it.

Let me know if that helps. Alternatively, it would be easier for me to help if you shared the code.

from bloc.

felangel avatar felangel commented on May 18, 2024

Closing this for now. Feel free to comment with more details and I will reopen it.

from bloc.

anderscheow avatar anderscheow commented on May 18, 2024

Is it possible to determine whether the stream is closed before dispatching any event?

from bloc.

felangel avatar felangel commented on May 18, 2024

No but why do you need to? The only way for the stream to be closed is if you called the bloc’s dispose method.

from bloc.

felangel avatar felangel commented on May 18, 2024

How are you disposing the bloc? Can you share any code?

from bloc.

anderscheow avatar anderscheow commented on May 18, 2024

My blocs are final in StatefulWidget

from bloc.

felangel avatar felangel commented on May 18, 2024

Is it possible to create a simplified version that illustrates the problem? It would make it much easier to help you.

from bloc.

felangel avatar felangel commented on May 18, 2024

Perfect, thanks!

from bloc.

anderscheow avatar anderscheow commented on May 18, 2024

One question, does the final bloc not dispose until the dispose() method is called although the StatefulWidget rebuild?

from bloc.

felangel avatar felangel commented on May 18, 2024

Hi @MohsinN thanks for the positive feedback! 👍

I looked at the issues you linked and it seems like the flutter team does not view that behavior as a bug (a widget should be able to be rebuilt every frame).

It would be really helpful if you can provide me with a simple flutter app which illustrates the problem.

from bloc.

JoseGeorges8 avatar JoseGeorges8 commented on May 18, 2024

Hi, I'm having the same issue!

I just started using flutter about a week ago and I have a 5 week time constraint for a big project, therefore I'm really sorry if it's just a silly mistake I can't figure out!

I got a TabView with three individual pages (stateful widget each) and each one receives data from an api therefore I'm creating a bloc on each of these pages.

I got two blocs so far, one is being used in two pages, let's call them A and B, and C with its own. It was one bloc covering the three pages but the issue appeared on the page C and me trying to debug it made me create another bloc thinking the dispose of the same bloc was being an issue in between pages, but as a result this individual C bloc is still receiving the issue!

This is my page C:

`class SupplementPlansPage extends StatefulWidget {
final SupplementPlanRepository supplementPlanRepository;

SupplementPlansPage({Key key, this.supplementPlanRepository})
: assert(supplementPlanRepository != null),
super(key: key);

@OverRide
_SupplementPlansPageState createState() => _SupplementPlansPageState();
}

class _SupplementPlansPageState extends State {
SupplementPlanBloc _planBloc;

@OverRide
void initState() {
super.initState();
_planBloc = SupplementPlanBloc(supplementPlanRepository: widget.supplementPlanRepository);
_planBloc.dispatch(FetchSupplementServings(clientId: 1));
}

@OverRide
Widget build(BuildContext context) {
return Stack(children: [
Container(
color: Color.fromRGBO(70, 79, 153, 1),
height: 80,
),
Padding(
padding: const EdgeInsets.all(16.0),
child: Card(
elevation: 2.0,
child: BlocBuilder(
bloc: planBloc,
builder: (
, SupplementPlanState state) {
if (state is SupplementPlanLoading)
return Center(child: CircularProgressIndicator());
if (state is SupplementServingsLoaded) {
final supplementIntakeTimeList = new List();
for(SupplementIntakeTime time in state.supplementIntakeTimeList.supplementIntakeTimes){
if(time.supplementServings.length > 0){
supplementIntakeTimeList.add(time);
}
}
return _buildSupplementIntakeTimeTitles(supplementIntakeTimeList);
}
if (state is SupplementPlanError)
return Text(
'Something went wrong!',
style: TextStyle(color: Colors.red),
);
},
),
),
),
]);
}

//Builds the listview
Widget _buildSupplementIntakeTimeTitles(List supplementIntakeTimeList) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
child: ListView.builder(
itemBuilder: (context, index) {
return _buildRow(supplementIntakeTimeList, index);
},
itemCount: supplementIntakeTimeList.length, // The amount of items being displayed
),
),
);
}

Widget _buildRow(List supplementIntakeTimeList, int index) {
return new SupplementIntakeTimeRow(supplementIntakeTimeList, index);
}

@OverRide
void dispose() {
_planBloc.dispose();
super.dispose();
}
}`

Worth saying that the three pages had with AutomaticKeepAliveClientMixin and the issue was present. I took it out for page C and issue is still present

Thanks in advance!

from bloc.

felangel avatar felangel commented on May 18, 2024

@JoseGeorges8 would it be possible to share the code with me? It would be much easier for me to help if you give me access to an application that has the problem you're running into. Thanks! 👍

from bloc.

JoseGeorges8 avatar JoseGeorges8 commented on May 18, 2024

Hey thanks for the quick response!

Unfortunately, I'm not allowed to add you as a collaborator, looking at the page where the issue is happening, is there any ideas you might think I could go ahead and jump on?

Thank you for understanding!

from bloc.

JoseGeorges8 avatar JoseGeorges8 commented on May 18, 2024

So I initialized the two blocs inside the TabView that's hosting the pages and passed them like this:

`
@OverRide
void initState() {

planBloc = PlanBloc(planRepository: planRepository);
supplementPlanBloc = SupplementPlanBloc(supplementPlanRepository: supplementPlanRepository);

mealPlansPage = MealPlansPage(planRepository: planRepository, planBloc: planBloc,);
workoutPlansPage = WorkoutPlansPage(planRepository: planRepository, planBloc: planBloc);
supplementPlansPage = SupplementPlansPage(supplementPlanRepository: supplementPlanRepository, supplementPlanBloc: supplementPlanBloc);
pages = [mealPlansPage, workoutPlansPage, supplementPlansPage];
currentPage = workoutPlansPage;
super.initState();
tabController = TabController(length: 3, vsync: this);

}

@OverRide
void dispose() {
tabController.dispose();
planBloc.dispose();
supplementPlanBloc.dispose();
super.dispose();
}
`

However the problem persist. It keeps on jumping from my StateLoading to my StateError (implemented the same way as your weather tutorial!) on page C, but the error is not being logged anymore. Also I took the with AutomaticKeepAliveClientMixin from every page in case it was affecting!

from bloc.

felangel avatar felangel commented on May 18, 2024

@JoseGeorges8 is there any way you can make a new super basic flutter app that has the same setup as your current app and has the same problem and share that with me?

You can just have a bunch of dummy blocs that don't really do anything and a bunch of empty pages. I just think having a concrete app that I can run and look at will make debugging your issue much easier. Thanks!

from bloc.

felangel avatar felangel commented on May 18, 2024

@JoseGeorges8 can you please describe the error and how I can reproduce it in the sample app?

from bloc.

JoseGeorges8 avatar JoseGeorges8 commented on May 18, 2024

Yes!

  1. Run the app
  2. Navigate from tab1 to tab3
  3. Back to tab1

You'll see a message displayed for when a state from a different event is received.

from bloc.

JoseGeorges8 avatar JoseGeorges8 commented on May 18, 2024

Sort of! How would I solve this problem then?

from bloc.

felangel avatar felangel commented on May 18, 2024

@JoseGeorges8 one other thing I noticed was Equatable wasn't being used properly. I'm about to open one more PR to fix the equatable usage and that should eliminate all rebuilds

from bloc.

felangel avatar felangel commented on May 18, 2024

@JoseGeorges8 Glad I was able to help 🎉

from bloc.

 avatar commented on May 18, 2024

@felangel still The Problem happens on Navigation,when i commented out the dispose()... overrides in a stateful widget,it doesn't happen!

from bloc.

felangel avatar felangel commented on May 18, 2024

@rajeshjeshar that means your bloc is being disposed too soon. I would recommend making sure the bloc is disposed by the widget that created it in order to avoid having a situation where a bloc is disposed too soon.

from bloc.

aBuder avatar aBuder commented on May 18, 2024

In my code I found the problem. It was the await statement before Navigator.

Produce the same error

   await  Navigator.push(
      context,
      MaterialPageRoute(
        builder: (BuildContext context) {
          return AppointmentSummaryScreen(
            AppointmentSummaryBloc(
              AppointmentRepository(),
            ),
            template,
            trainer,
            time,
          );
        },
      ),
    );

Produce no error

    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (BuildContext context) {
          return AppointmentSummaryScreen(
            AppointmentSummaryBloc(
              AppointmentRepository(),
            ),
            template,
            trainer,
            time,
          );
        },
      ),
    );

from bloc.

enricobenedos avatar enricobenedos commented on May 18, 2024

Good morning guys,
I've the same problem explained in this issue:
StateError (Bad state: Cannot add new events after calling close)
I think my problem is similar to the @JoseGeorges8 problem. I have a drawer that provide to navigate on all the App pages. In page 1 and page 2 I need to fetch the same data. In this case I need to call myblock.fetchAllData() on all the two pages initState(), then always in the same 2 pages on dispose() method I call myBlock.dispose(). I haven't understand very well because I can't re-add a new event after the dispose. For me is not clear which type of changes I need to do for fix my code remaining in the bloc pattern side.

from bloc.

felangel avatar felangel commented on May 18, 2024

@enricobenedos as @craiglabenz mentioned you should make sure you don't dispose your bloc if it's still needed. I would recommend disposing your bloc in the same widget that instantiated it. If after these explanations you're still having trouble, it'd be awesome if you could share a link to a sample app which demonstrates the issue you're running into.

Thanks!

from bloc.

enricobenedos avatar enricobenedos commented on May 18, 2024

thank you @craiglabenz for your explanation. Now I need to study how to use the BlocProvider and the first solution that I think to use is to doesn't dispose the blocs because in my case they are app wide. But this is totally wrong due to the @felangel suggestion (' I would recommend disposing your bloc in the same widget that instantiated it'). If I will find some time I will provide you a sample project.

from bloc.

enricobenedos avatar enricobenedos commented on May 18, 2024

Only for show you this is my basic implementation of bloc pattern: MyProject. In this case I think it is correct to not dispose anything because my bloc need to be alive during all the app life.

from bloc.

felangel avatar felangel commented on May 18, 2024

@enricobenedos thanks for sharing. I noticed your project does not use the bloc or flutter_bloc library so I'm a bit confused. This repo is for issues related to the bloc/flutter_bloc/angular_bloc packages.

Anyway, I would recommend changing your MyApp widget to a StatefulWidget and creating/disposing the DogBloc there. Then you can use an InheritedWidget (in the flutter_bloc library it's called BlocProvider) to provide your bloc instance to the widgets in the sub-tree.

from bloc.

rrifafauzikomara avatar rrifafauzikomara commented on May 18, 2024

Hi @felangel

I have an error too like : this issue. This is my BLoC code : https://pastebin.com/Tx24rGA4
and this is my detail_screen who implemented the BLoC : https://pastebin.com/DxTNwPpD

I hope u can help me for best practice using BLoC and solve this issue, Because if i deleted method dispose in detail screen, the apps is always working

from bloc.

felangel avatar felangel commented on May 18, 2024

@rrifafauzikomara for future reference please refrain from asking questions that aren't directly related to the bloc library here.

Regarding your question, you need to make sure you call dispose in the StatefulWidget where the Bloc is created. In your case you are creating the bloc instance inside of the bloc file which is wrong. Instead, you should create the bloc in initState of your detail screen and then you can uncomment the dispose and it should all work normally.

from bloc.

rrifafauzikomara avatar rrifafauzikomara commented on May 18, 2024

Okee, sorry about that @felangel . Because I search about my error then link to here. So I ask to you because u solve the problem. But in my case is different code and I hope you can help me for fixing my error hehe.

Btw, can u help me for giving me solution what must I edit? Because I read your instruction and still not understand what must I do for that.

from bloc.

rrifafauzikomara avatar rrifafauzikomara commented on May 18, 2024

Oh man, thank you very much @felangel . It works very well on my application. You're like an expert in the field of BLoC. Btw can you write this answer to a question I made on StackOverflow? So that I can resolve the question and hopefully can help others.

Link: This

And I want to ask your opinion, by looking at the code I use BLoC is this good? Or need change? Because I saw the article that you created at: This you separated the event, state, and bloc.

from bloc.

nerder avatar nerder commented on May 18, 2024

@rrifafauzikomara you need to remove final bloc = MealsDetailBloc(); from your detail bloc and instead in your widget do something like:

final bloc = MealsDetailBloc();

@override
void initState() {
  super.initState();
  bloc.fetchDetailMeals(widget.idMeal);
}
 
@override
void dispose() {
  bloc.dispose();
  super.dispose();
}

This way your widget is creating and disposing the bloc. Before, the widget only disposes the bloc but it is never re-created when the widget is re-created. Hope that helps 👍

Hey @felangel this is still the case with the new close method? I should call it manually on dispose of my StatefulWidget?

An example might be:

@override
void initState() {
  super.initState();
  myBloc.fetchStuff();
}

@override
void dispose() {
  myBloc.close();
  super.dispose();
}

I'm getting a similar error and i'm not sure on how to handle it with v2.0.x

from bloc.

jacinto-joao avatar jacinto-joao commented on May 18, 2024

Hi, @felangel I know that I have commented about this in another git link.

but this error is a really big bang bang, and give us a lot of headaches.
This unpredictable when it will fire bad Cannot add new events after calling close. believe me that I have followed your best practice and all the solutions you have provided.

But I still get this error, it sometimes fires and sometimes doesn't.

I'm coming from web dev and used the Vuejs which use vuex as state management, and when I saw your library, it was the only thing that comes closer to vuex.

But the beaut of vuex, there's a global way to access any state and it's namespaced.

you don't need to pass as parameters the current instance to another page.

The only thing you do is just access the state via the namespace and dispatch or get data.
whenever you trigger the actions it will make sure to update all instances.

But with the block library, or flutter things are really mess or trick to get it right.
while trying to access or trigger some actions, and it's so stressful, when you cannot control or have a little an idea why things happen in that way.

So resuming maybe is a good thing about Cannot add new events after calling close but this is a big mess to deal with.

Sorry for the post, I just don't know how to explain my current problem as it has been explained with others, even though still getting same results.

This issue in most case scenarios happens with multiple pages using the same bloc.

Like you have the App bar that has notifications counter listener, you have other pages details that listen to the bloc, you have filter page that dispatch filter action to then update data loaded state.

All these things were supposed to work together, anyway.
Either way, I still love this library.

from bloc.

vincentngo avatar vincentngo commented on May 18, 2024

Check out JoseGeorges8/bloc_issue#1

I'd recommend having a bloc for each tab state along with AutomaticKeepAliveClientMixin to reduce the number of rebuilds.

Let me know if that helps!

@felangel thank you!!!! lol

from bloc.

hackerunet avatar hackerunet commented on May 18, 2024

I'm currently facing this issue, so apparently i'm closing the bloc early before I finish using them. But, not sure why bloc get's disposed if they are declared in MultiBloc Provider in main.dart, The time when this error shows up, is during a build in a child object. So my question is, how do I close them out of the child widgets?

runApp(MultiBlocProvider(
    providers: [
      BlocProvider<AuthenticationBloc>(
          create: (context) {
            return AuthenticationBloc(
                authenticationService: authenticationService)
              ..add(AppStarted());
          },
          child: App(authenticationService: authenticationService)),
      BlocProvider<NavigationBloc>(
        create: (context) {
          return NavigationBloc()..add(RegistrationStarted());
        },
        child: RegistrationView(),
      ),
      BlocProvider<RegistrationBloc>(
        create: (context) {
          return RegistrationBloc()..add(RegistrationFormEvent());
        },
        child: UserDataView(),
      )
    ],
    child: App(authenticationService: authenticationService),
  ));

I have it declared in the main application, which means I should close blocs when moving away to a different view out of those childs. so, how do I close them and where?

from bloc.

felangel avatar felangel commented on May 18, 2024

Hi @hackerunet you shouldn't need to close them manually; BlocProvider will handle disposing them when BlocProvider is disposed. Can you share a link to a sample app which illustrates the problem you're having? Thanks! 👍

from bloc.

hackerunet avatar hackerunet commented on May 18, 2024

I just find out what was going on, basically I was calling the dispose method manually and that was wrong because the one disposing the streams is the provider.
So removing dispose from my view fixed it.
I hope the resources get closed by the provider as you say because I don't have a clue how would I debug a memory leak caused by wrongly handled blocs.

from bloc.

felangel avatar felangel commented on May 18, 2024

@hackerunet as long as you are creating the bloc inside the create of BlocProvider it will be disposed automatically and you have nothing to worry about 😄

from bloc.

GyuriMajercsik avatar GyuriMajercsik commented on May 18, 2024

I just find out what was going on, basically I was calling the dispose method manually and that was wrong because the one disposing the streams is the provider.
So removing dispose from my view fixed it.
I hope the resources get closed by the provider as you say because I don't have a clue how would I debug a memory leak caused by wrongly handled blocs.

You can override the close() method in your bloc and print something when is called. Also, you can do additional clean-up there.

@override
Future<void> close() async {
 super.close();
 print('Closing Bloc');
 // todo additional clean-up
}

from bloc.

GyuriMajercsik avatar GyuriMajercsik commented on May 18, 2024

Hi @felangel ,

I experienced this issue as well on hot reload, and it was unexpected.
After some investigation I managed to reproduce it using flutter_bloc_with_stream example.

I modified the main.dart and added dialog_test.dart file.
Please let me know if this is a bug or just a misuse of bloc plugin.

The following error appears when displaying the dialog and pressing hot-reload:

Unhandled error Bad state: Cannot add new events after calling close occurred in bloc Instance of 'TickerBloc'.


main.dart

import 'package:flutter/material.dart';

import 'dialog_test.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Bloc with Streams'),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          showGeneralDialog(
            context: context,
            pageBuilder: (
              BuildContext buildContext,
              Animation<double> animation,
              Animation<double> secondaryAnimation,
            ) {
              return Builder(builder: (context) {
                return AlertDialog(
                  key: UniqueKey(), // IMPORTANT: without unique key the issue will not happen
                  content: DialogTest(),
                );
              });
            },
            barrierDismissible: false,
            barrierLabel:
                MaterialLocalizations.of(context).modalBarrierDismissLabel,
            barrierColor: Colors.black54,
            transitionDuration: const Duration(milliseconds: 500),
            transitionBuilder: _buildDialogTransitions,
          );
        },
        tooltip: 'Start',
        child: Icon(Icons.timer),
      ),
    );
  }

  Widget _buildDialogTransitions(
      BuildContext context,
      Animation<double> animation,
      Animation<double> secondaryAnimation,
      Widget child) {
    // IMPORTANT: without layout builder here it will not happen
    return LayoutBuilder(builder: (context, constraint) => child);
  }
}

dialog_test.dart

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

import 'bloc/ticker_bloc.dart';
import 'ticker/ticker.dart';

class DialogTest extends StatefulWidget {
  @override
  _DialogTestState createState() => _DialogTestState();
}

class _DialogTestState extends State<DialogTest> {
  @override
  Widget build(BuildContext context) {
    return MultiBlocProvider(
      providers: [
        BlocProvider(create: (context) {
          var tickerBloc = TickerBloc(Ticker());
          tickerBloc.add(TickerStarted());

          return tickerBloc;
        })
      ],
      child: BlocBuilder<TickerBloc, TickerState>(
        builder: (context, state) {
          int count;
          if (state is TickerTickSuccess) {
            count = state.count;
          }

          return Container(
            margin: EdgeInsets.all(100),
            color: Colors.green,
            child:
                Center(child: Text(count == null ? 'Not started ' : '$count')),
          );
        },
      ),
    );
  }
}

from bloc.

xuhaibahmad avatar xuhaibahmad commented on May 18, 2024

I was pulling my hair all day due to this issue. I tried almost every solution but in the end, it was a stupid and unrelated mistake on my part.

For future reference, if anyone of you is using Injectable package for DI, make sure you don't mark any of your Blocs as a singleton. It will work the first time but break when you try to revisit that screen due to the DI forcing the widget to reuse the disposed bloc instance.

from bloc.

narcodico avatar narcodico commented on May 18, 2024

@xuhaibahmad you can mark your bloc as singleton as long as you make sure to reset the instance from get_it when closing the bloc. That will force get_it to create a new singleton instance when requested thereafter.

You can mix in this on your bloc and it will take care of it:

mixin AutoResetLazySingleton<E, S> on Bloc<E, S> {
  @override
  Future<void> close() {
    if (locator.isRegistered<Bloc<E, S>>(instance: this)) {
      locator.resetLazySingleton<Bloc<E, S>>(instance: this);
    }
    return super.close();
  }
}

from bloc.

av2ry-1030 avatar av2ry-1030 commented on May 18, 2024

Hello @felangel I'm facing the same issue, I have a profile page that uses ProfilBloc who's responsible for charging user and his posts. For the editing of user I'm using another bloc called EditBloc. I'm declaring an EditBloc in profile page and passing it trough parameter's while navigating to the EditPage. I also have a StreamSubscription who listens the states of EditBloc in my profile page for the purpose of refreshing user's data. The editing and the refresh are made but I'm getting this error after going back to ProfilPage and trying to remake a change.

Here is my profilePage :

class _ProfileState extends State<Profile> with TickerProviderStateMixin {
  ProfileBloc profilBloc;
  StreamSubscription editBlocSubscription;
  EditBloc editBloc = EditBloc();


@override
  void initState() {
    super.initState();

     editBlocSubscription = editBloc.listen((state){
      if(state is EditDone){
          setState(() {
          profilBloc.user = editBloc.getUser();
          
        });
      }
    });

Navigation to EditProfile :

Future pushToEditProfil(context) async{ Navigator.push((context), MaterialPageRoute( builder: (context) => EditProfileScreen(editBloc: editBloc, user: profilBloc.user,))); }

Here is my EditProfile :

class _ProfilEditState extends State<EditProfil> {

  EditBloc editBloc;
  User user;
  _ProfilEditState({
    @required this.editBloc,
    @required this.user,
  });
  @override
  void initState() {
    super.initState();
    editBloc = BlocProvider.of<EditBloc>(context);
    editBloc.user = user;
  }

  @override
  void dispose() {
    editBloc.close();
    super.dispose();
  }


  @override
  Widget build(BuildContext context) {
    return BlocListener<EditBloc, EditState>(
        listener: (context, state) {
          if (state is EditDone) {
            setState(() {
              user = editBloc.user;
            });
          }
        },
        child: Scaffold(
          appBar: AppBar(
            backgroundColor: Colors.white,
            centerTitle: true,
            leading: IconButton(
                icon: Icon(Icons.arrow_back, color: Colors.black),
                onPressed: () => buildPop(context)),
            title: Text("Edit Profil",
                style: TextStyle(
                    color: Colors.black, fontWeight: FontWeight.bold)),
          ),
          body: WillPopScope(
            onWillPop: (){
              Navigator.pop(context);
            },
            child: buildColumn(context),
          )
        )
    );
      
  }

  Column buildColumn(BuildContext context ) {
    return Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          children: <Widget>[
            Column(
              children: <Widget>[
                Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: CircleAvatar(
                    backgroundImage: NetworkImage(
                        'https://is1-ssl.mzstatic.com/image/thumb/Purple113/v4/27/e0/07/27e00727-f3c7-1c7d-4ccc-d5c8e9609bf6/source/512x512bb.jpg'),
                    radius: 40.0,
                  ),
                ),
                Text("Change photo")
              ],
            ),
            GestureDetector(
              onTap: () =>
                  Navigator.push(
                    (context),
                    MaterialPageRoute<EditName>(
                      builder: (_) =>
                          BlocProvider.value(
                            value: editBloc,
                            child: EditName(name: user.name),
                          ),
                    ),
                  ),
              child: _Container("Name", user.name),
            ),
            GestureDetector(
              onTap: () =>
                  Navigator.of(context).push(
                    MaterialPageRoute<EditUsername>(
                      builder: (_) =>
                          BlocProvider.value(
                            value: BlocProvider.of<EditBloc>(context),
                            child: EditUsername(userName: user.username),
                          ),
                    ),
                  ),
              child: _Container("Username", user.username),
            ),
            GestureDetector(
              onTap: () =>
                  Navigator.of(context).push(
                    MaterialPageRoute<EditBioScreen>(
                      builder: (_) =>
                          BlocProvider.value(
                            value: BlocProvider.of<EditBloc>(context),
                            child: EditBioScreen(),
                          ),
                    ),
                  ),
              child: _Container("Bio", user.bio),
            ),
          ],
        );
  }

and here is for example EditName page :

class EditName extends StatefulWidget {
  String name;

  EditName({Key key, @required this.name}) : super(key: key);
  @override
  State<StatefulWidget> createState() => _EditNameState(name);
}

class _EditNameState extends State<EditName> {
  _EditNameState(this.name);
  final String name;
  EditBloc editBloc;
  var editView = EditView(null, null, 1);

  @override
  void initState() {
    super.initState();
    editBloc = BlocProvider.of<EditBloc>(context);
    editView.info = editBloc.getUser().name;
    editView.maxLength = 50;
  }

  @override
  void dispose() {
    editBloc.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return BlocListener<EditBloc, EditState>(
      listener: (context, state) {
        if (state is EditDone) {
      
          Navigator.pop(context);
        }
      },
      child: Scaffold(
        appBar: AppBar(
          backgroundColor: Colors.white,
          centerTitle: true,
          leading: FlatButton(
              child: (Text("X", style: TextStyle(color: Colors.black))),
              onPressed: () => Navigator.pop(context)),
          title: Text("Name",
              style:
                  TextStyle(color: Colors.black, fontWeight: FontWeight.bold)),
          actions: <Widget>[
            FlatButton(
              child: Text("Save"),
              onPressed: () => !editView.isValid ? null : editBloc.add(EditSaveName(editView.getInfo)),
            ),
          ],
        ),
        body: Container(
          padding: EdgeInsets.only(left: 10.0, right: 10.0, top: 10.0),
          child: Column(
            children: <Widget>[
              Align(
                alignment: Alignment.topLeft,
                child: Text("Name",
                    style: TextStyle(color: Colors.blueGrey, fontSize: 15)),
              ),
              editView,
            ],
          ),
        ),
      ),
    );

  }
}

Have you an idea of what I'm doing wrong? Thanks a lot!

from bloc.

enricobenedos avatar enricobenedos commented on May 18, 2024

Hello @av2ry-1030, at the moment I have no time to rewrite you the correct code. If you give me a link to clone your sample project I can help you.
Anyway there are some issue in your code, the first that it the same that cause this error is related to editBloc.close();. If EditBloc is created by ProfilePage you need to dispose your bloc in this page. You cannot call editBloc.close(); in EditProfile page if you need to use its state in ProfilePage.
Another strange thing is that you are telling us that you need to pass EditBloc to EditProfile page but you need to choose if you want to pass it through the constructor (I think it is not a good pattern using this library) or use the context to keep it.
In your sample your are mixing these two different approaches because first you require that editBloc is passed through constructor

_ProfilEditState({
    @required this.editBloc,
    @required this.user,
  });

but then you overwrite it using context to retrieve it

editBloc = BlocProvider.of<EditBloc>(context);

I think that reading docs and examples can really help you to better understand and study this library. You are very lucky because documentation is written very well!

from bloc.

narcodico avatar narcodico commented on May 18, 2024

Hi @av2ry-1030 👋

I suggest you upgrade to v5.0.0 of bloc library and your problem should be fixed. 👍

from bloc.

av2ry-1030 avatar av2ry-1030 commented on May 18, 2024

Hello @enricobenedos oh yes you're right. I tried so many things in order to find the problem that I wrote theses non-senses.. ^^'. Thank you.

I will first try to upgrade the library to v5.0 like @RollyPeres suggested and see what happens.

from bloc.

av2ry-1030 avatar av2ry-1030 commented on May 18, 2024

Hello @RollyPeres thank you for the suggestion, I tried to upgrade but it seem's that there is an problem of compatibility in the library.

The current Flutter SDK version is 1.12.13+hotfix.8.

Because flutter_bloc >=5.0.0 depends on flutter_cubit ^0.1.0 which depends on provider ^4.1.3, flutter_bloc >=5.0.0 requires provider ^4.1.3.
So, because provider >=4.1.0-dev+1 requires Flutter SDK version >=1.15.17-pre.5 and lit_app depends on flutter_bloc ^5.0.0, version solving failed.
pub get failed (1; So, because provider >=4.1.0-dev+1 requires Flutter SDK version >=1.15.17-pre.5 and lit_app depends on flutter_bloc ^5.0.0, version solving failed.)`

from bloc.

felangel avatar felangel commented on May 18, 2024

@av2ry-1030 looks like you need to upgrade Flutter. The version you're on is quite old (the current stable is at 1.17.4).

flutter upgrade

from bloc.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.