felangel / bloc

A predictable state management library that helps implement the BLoC design pattern
https://bloclibrary.dev
MIT License
11.8k stars 3.39k forks source link

Subsequent calls to the same cubit don't emit the fresh state #2004

Closed MillerAdulu closed 3 years ago

MillerAdulu commented 3 years ago

Describe the bug I have an application where I am using the get_it service locator and for the past month, I have run into an issue which has proven hard to resolve. When I call a cubit twice in different places, it doesn't do the correct thing. The first time works but the second time it doesn't emit the correct state to rebuild the UI.

To Reproduce Steps to reproduce the behaviour:

  1. Please find my stripped-down use case that I have in this repository. https://github.com/MillerAdulu/cubit_state
  2. cd cubit_state
  3. Run Code Generations: make gen or copy the command from the Makefile if Make isn't installed
  4. Run the app: make

Expected behavior When I call the cubit from another screen, it should start all over from the initial state.

Screenshots

Logs Run flutter analyze and attach any output of that command below. No issues found! (ran in 304.8s) If there are any analysis errors, try resolving them before filing this issue.

Additional context I am using the get_it package to register cubits so that I don't do it all over. I am open to providing another use-case, this time with a form which speaks more into why I prefer to use get_it. I have added two TODO's that allow you to enable the hack to get it to work as expected. My issue with the hack is, I have a very complex app with more than 50 cubits and I don't' want to be doing that each time. So I am looking for a better approach.

In this example I have used the case where I switch screens, the same thing happens when I switch tabs. I thought it was an issue with state being emitted, so I moved from using Equatable to freezed and moved my built_value models to freezed also but still the same thing happens.

Paste the output of running flutter doctor -v here.


    • Flutter version 1.22.4 at C:\flutter
    • Framework revision 1aafb3a8b9 (4 weeks ago), 2020-11-13 09:59:28 -0800
    • Engine revision 2c956a31c0
    • Dart version 2.10.4       

[√] Android toolchain - develop for Android devices (Android SDK version 30.0.0) 
    • Android SDK at C:\Users\adulu\AppData\Local\Android\sdk
    • Platform android-30, build-tools 30.0.0
    • ANDROID_HOME = C:\Users\adulu\AppData\Local\Android\sdk
    • Java binary at: C:\Program Files\Android\Android Studio\jre\bin\java       
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b01)
    • All Android licenses accepted.

[√] Android Studio (version 4.0)
    • Android Studio at C:\Program Files\Android\Android Studio
    • Flutter plugin version 48.0.2
    • Dart plugin version 193.7361
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b01)

[√] VS Code (version 1.51.1)
    • VS Code at C:\Users\adulu\AppData\Local\Programs\Microsoft VS Code
    • Flutter extension version 3.17.0

[!] Connected device      
    ! No devices available

! Doctor found issues in 1 category. ```
rodrigobastosv commented 3 years ago

When I call the cubit from another screen, it should start all over from the initial state.

As you are using get_it to inject the cubits, whats probably happening is that you are reusing the same cubit throuhout the whole app. Since you are doing this you are never recreating the cubits, and because of that you are never emiting the initial state of them.

If you want to start all over the initial state i highly recommend you recreate your cubit. You can create a structure like Page and View, that the only job of the Page widget is to create and pass a brand new cubit to the view

MillerAdulu commented 3 years ago

@rodrigobastosv Thanks for your input. From what you are saying, I understand that I need to find a way to recreate the cubit each time after it's done.

I am not familiar with the Page and View architecture that you are proposing I try out. Might you have a sample I can look at and deduce from? Or links if you don't have that much time on your hands?

rodrigobastosv commented 3 years ago

Sure @MillerAdulu , something like that:

class TodosPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (_) => TodosCubit(),
      child: TodosView(),
    );
  }
}

class TodosView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: BlocBuilder<TodosCubit, TodosState>(
        builder: (context, state) => ListView(),
      ),
    );
  }
}

You can see that everytime you are entering the TodosPage, you recreate a cubit from scratch. With that you will emit the initial state of the cubit/bloc, and i believe your problem will be solved.

MillerAdulu commented 3 years ago

Aha! Thank you @rodrigobastosv. I didn't think that this was the Page-View pattern. Thank you. From the look of things, it looks like I shall have to dump get_it and for this.

I really liked the use of get_it, but I have to do what needs to be done. Thanks for the prompt assistance.

MillerAdulu commented 3 years ago

For anyone else looking at this issue who still desires to use get_it cause it would be tedious to perform code injection per widget, there's this particular method that allows a reset of a particular registered cubit. A good place to place it is in the dispose method.

https://github.com/fluttercommunity/get_it#resetting-lazysingletons

iampato commented 3 years ago

If you are using the same cubit in multiple place use context.read() << with extensions>> or BlocProvider.of(context)