Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug?] Listener called when being rebuilt, even though no state changes #368

Closed
matanshukry opened this issue Jun 22, 2019 · 17 comments
Closed
Assignees
Labels
bug Something isn't working
Projects

Comments

@matanshukry
Copy link

Describe the bug
I have 2 pages with BlocListener in each of them. The Blocs them selves are kept in the
I have a BlocListener on my widget

To Reproduce
Create an app widget with blocs in the state.
Create 2 pages that you can navigate between them
In each page have a BlocListener widget on a state. Make sure the state doesn't change when navigating.

The 'listener' callback will be called every time the BlocListener is rebuilt, even though the state is the same.

Expected behavior
The listener callback in BlocListener should only be called once when the state changes, regardless if the BlocListener was just built or not.

**Logs **

$ flutter analyze
Analyzing my-app...
No issues found! (ran in 5.5s)

Paste the output of running flutter doctor -v here.

[√] Flutter (Channel dev, v1.7.4, on Microsoft Windows [Version 10.0.18362.175], locale en-US)
    • Flutter version 1.7.4 at C:\Flutter
    • Framework revision dfecafa4ab (7 days ago), 2019-06-14 09:46:23 -0700
    • Engine revision 2589785b5c
    • Dart version 2.4.0

[√] Android toolchain - develop for Android devices (Android SDK version 28.0.3)
    • Android SDK at C:\Android\Sdk
    • Android NDK location not configured (optional; useful for native profiling support)
    • Platform android-28, build-tools 28.0.3
    • ANDROID_HOME = C:\Android\Sdk
    • Java binary at: C:\Program Files\Android\Android Studio\jre\bin\java
    • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1343-b01)
    • All Android licenses accepted.

[√] Visual Studio - develop for Windows (Visual Studio Community 2017 15.9.10)
    • Visual Studio at C:\Program Files (x86)\Microsoft Visual Studio\2017\Community
    • Visual Studio Community 2017 version 15.9.28307.557

[√] Android Studio (version 3.4)
    • Android Studio at C:\Program Files\Android\Android Studio
    • Flutter plugin version 36.1.1
    • Dart plugin version 183.6270
    • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1343-b01)

[√] IntelliJ IDEA Community Edition (version 2019.1)
    • IntelliJ at C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2018.1.2
    • Flutter plugin version 36.0.4
    • Dart plugin version 191.7830

[√] Connected device (1 available)
    • Android SDK built for x86 • emulator-5554 • android-x86 • Android 9 (API 28) (emulator)

• No issues found!
@matanshukry matanshukry changed the title [Bug?] Listener called first time when being rebuilt, even though no state change [Bug?] Listener called when being rebuilt, even though no state change Jun 22, 2019
@matanshukry matanshukry changed the title [Bug?] Listener called when being rebuilt, even though no state change [Bug?] Listener called when being rebuilt, even though no state changes Jun 22, 2019
@felangel felangel added this to In progress in bloc Jun 23, 2019
@felangel felangel moved this from In progress to To do in bloc Jun 23, 2019
@felangel felangel self-assigned this Jun 23, 2019
@felangel felangel added the investigating Investigating the issue label Jun 23, 2019
@felangel felangel moved this from To do to In progress in bloc Jun 23, 2019
@felangel felangel added bug Something isn't working in progress and removed investigating Investigating the issue labels Jun 23, 2019
@felangel
Copy link
Owner

Fixed by #370 and released in flutter_bloc v0.18.2 🎉

@felangel felangel moved this from In progress to Done in bloc Jun 24, 2019
@biklas7
Copy link

biklas7 commented Jun 27, 2019

Hello @felangel. First of all, thank you for this awesome package!

Regarding this issue. You say in the readme file that: listener is only called once for each state change (including initialState) unlike builder in BlocBuilder and is a void function. I think that with this change the listener is not triggered for the initialState. Can you confirm this?

@felangel
Copy link
Owner

felangel commented Jun 27, 2019

Hey @biklas7 👋

Yeah you're totally right! I need to update the documentation to say listener is only called once for each state change (not including initialState) unlike builder in BlocBuilder and is a void function. Thanks for pointing this out 💯 (fixed in bedd754)

@biklas7
Copy link

biklas7 commented Jun 27, 2019

Maybe I shouldn't put this here but in a project that I'm working on, I was relying on the listener to be called for the initialState to dispatch an event. Since this is not possible, what do you suggest us to do? Maybe convert to a StatefulWidget and dispatch the event in the initState? I don't think this is the best solution 🤔

@felangel
Copy link
Owner

@biklas7 you can just dispatch the event right when the bloc is created like so:

BlocProvider(
  builder: (context) => MyBloc()..dispatch(MyBlocEvent()),
  child: Container(),
)

@biklas7
Copy link

biklas7 commented Jun 27, 2019

@felangel thank you 👍 Keep up the awesome work on your packages!

@bobwiller
Copy link

@felangel can you provide some guidance around when to use BlocListener vs BlocBuilder? If "BlocBuilder analogous to StreamBuilder", what is BlocListener analogous to?
Thx

@felangel
Copy link
Owner

felangel commented Jun 28, 2019

@bobwiller BlocBuilder should only be used to return different widgets based on the bloc state. For things like navigation, showing alerts, showing SnackBars, etc ... you should use BlocListener. This is because BlocBuilder can be called many times by the flutter framework whereas BlocListener is only called once per state change. BlocListener is analogous to manually listening to changes in the bloc state and disposing the subscription.

Check out the SnackBar Recipe for more detail 👍

@bobwiller
Copy link

@felangel thx - i feel like the warning at the bottom of that recipe ("We should NEVER....") should be in the BlocBuilder documentation and/or have a reference to that recipe in the BlocListener doc page!

One last question: I feel like you moved to a pattern where you do:
bloc: BlocProvider.of<DataBloc>(context)

...when you need a reference to the Bloc. So in that recipe, you make that call twice - once in the Listener and again in the Builder vs. getting the reference in the constructor like this:
DataBloc _dataBloc = BlocProvider.of<DataBloc>(context)

and just reusing the reference when you need it. I assume there is a reason for this?

@felangel
Copy link
Owner

Good point! I’ll make that change 👍

You could get the instance in the build method and reuse the reference in both places. You can’t use BlocProvider.of(context) in the constructor because you need to have access to the BuildContext.

The lookup is O(1) so it shouldn’t have any performance impact but I will make that change as well. 👍

@UliPrantz
Copy link

I think the bug was reintroduced. I just built an app with BlocListener for my navigation. After I experienced some strange behaviour I put a print(state) statement inside my listener function and the output was something like this:

flutter: AuthInProgress
flutter: AuthInProgress
flutter: GoogleLoginSuccessful
flutter: GoogleLoginSuccessful

So my listener function is called twice for each state which really is a problem since my navigation is placed inside that listener some routes get pushed twice within my navigator.

My App is currently too complex and I haven't had enough time to investigate everything but it really seems like the bug just somehow was reintroduced. Maybe the Reproduce Part of the original issue already reproduces the issue again (I haven't checked yet).

@felangel
Copy link
Owner

@UliPrantz can you put together a sample app which reproduces the issue? Thanks!

@UliPrantz
Copy link

UliPrantz commented Oct 22, 2019

@felangel I try to do it rn. Just could take some time. One simple question that I don't misunderstand the documentation: If my bloc yields the same state twice one after another (with no other state in between) then the listener function is only called once, isn't it? (My actual bloc doesn't do this but it would be easier to write the example app to reproduce the bug)

@tenhobi
Copy link
Collaborator

tenhobi commented Oct 22, 2019

@UliPrantz yes, but only if the states can be compared and are evaulated as the same. Check package:equatable if you want to evaulate states with the same data as the same object. 👍

@UliPrantz
Copy link

UliPrantz commented Oct 22, 2019

@tenhobi ok the thing is I know equatable just a little bit. So the actual State that I'm using is super simple:

class BaseBlocState {}
class MyState extends BaseBlocState {
  @override
  String toString() => 'MyState';
}

and if the function body of my mapEventToState(BaseBlocEvent event) would look like this:

if (event is MyEvent) {
  yield MyState();
  yield MyState();
}

this would call the listener function only once as far as I understand.

@UliPrantz
Copy link

UliPrantz commented Oct 22, 2019

So I just wrote the app accordingly to my description above and this would call the listener function twice I can also share the github project. (update: this is the git repo for the sample app https://github.com/UliPrantz/TestRepo)

@hammad-tariq
Copy link

@bobwiller BlocBuilder should only be used to return different widgets based on the bloc state. For things like navigation, showing alerts, showing SnackBars, etc ... you should use BlocListener. This is because BlocBuilder can be called many times by the flutter framework whereas BlocListener is only called once per state change. BlocListener is analogous to manually listening to changes in the bloc state and disposing the subscription.

Check out the SnackBar Recipe for more detail 👍

Link Updated:
https://bloclibrary.dev/#/recipesfluttershowsnackbar

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
bloc
  
Done
Development

No branches or pull requests

7 participants