I have an issue with repeated states in BlocListener
.
I have two blocs
: MainBloc
which any page can use, and FirstPageBloc
only for FirstPage
.
I also have two pages: FirstPage
and SecondPage
. Each page has only one button "Go to another page". And I disabled the MaterialPageRoute
animation for them, so you can change pages very quickly.
The flow:
FirstPage.initState()
adds FirstFirstPageEvent
(event of FirstPageBloc
). FirstPageBloc
adds FirstMainEvent
(event of MainBloc
). MainBloc
yield
s FirstMainState
.FirstPage
has BlocListener
which listen for FirstMainState
, so it receives FirstMainState
.If you change pages from FirstPage
to SecondPage
slowly then all fine, but when you use literally double click for it(one click per one page change) then FirstPage
will receive FirstState
twice, so you can see two in a row log messages First page received FirstState
. But MainBloc
yield
s FirstMainState
only once.
Please, check this minimal working example app.
Hi @don-prog โ
I had a quick look at your sample and from what I see you're navigating to second page with pushReplacementNamed
which will destroy your first page then create the second one.
In your second page you're doing pushNamed
on first page, which means your first page gets created again.
This is really important because your initState
in your first page will run twice, thus adding that event twice.
Consider navigating to second page using pushNamed
and going back to the first page using pop
.
Hope that helps! ๐
Closing for now but feel free to comment with additional questions if you're still having trouble ๐
Thanks so much @RollyPeres for taking the time to answer ๐
@RollyPeres thanks for your suggestion! But:
Navigator
commands in the FirstPage
and SecondPage
- just a typo which I fixed in the last commit, and this does not change anything._FirstPageState.initState()
again, but not twice. What I mean:I/flutter (28302): _FirstPageState.initState()
I/flutter (28302): _firstPageBloc.add(FirstFirstPageEvent())
I/flutter (28302): FirstPage.buildBody()
I/flutter (28302): sl().add(FirstMainEvent())
I/flutter (28302): yield FirstState
I/flutter (28302): First page received FirstState
I/flutter (28302): First page received FirstState
So, you can see two First page received FirstState
messages, but only one message for the rest of the commands.
Does that make more sense?
I've opened a PR https://github.com/don-prog/bloc_repeated_states_issue/pull/1
Since you haven't detailed what exactly you're trying to achieve, I assumed you want to be able to add an event when you come back from second page, so I showcased how that could be achieved.
With my implementation the first event is only added once, when the first page is created.
Hope it solves your issue ๐
@RollyPeres thanks for your PR! But:
pop()
, but I can't use this scenario. The real app has BottomNavigationBar
, when the user clicks on the tab then old tab should be closed. Buttons in this app - simulation of the tab buttons, so I can't just pop
, because the stack is already clear.Navigator
scenario.@don-prog You still haven't detailed what you want to achieve exactly. If you need to achieve something around a BottomNavigationBar
then I'd advice you to submit your exact scenario as an example with a clear and concise explanation of what you're trying to achieve.
@RollyPeres I just want to achieve basic bottom navigation with a clear stack(clicking on the tab button not just pushes, but also clears the stack). Please, check the new commit.
You can achieve that pretty easy without needing to explicitly navigate.
This is one quick example I found: https://medium.com/@uncoded_decimal/creating-bottom-navigation-tabs-using-flutter-2286681450d4
By default a tab will be destroyed when navigating away and recreated when going back to it.
Hope this helps you achieve your desired outcome ๐
@RollyPeres thanks again, but this is just a workaround. I have seen many similar things.
Besides, what if I want to use a described Navigator scenario even just with buttons. Is it incorrect and why?
Do you know how to solve the described problem or what exactly causes it?
@don-prog it's not a workaround, it's a standard way of using tabs in a bottom navbaresque way.
I'm sorry but I really can't understand where you're going with all this. I don't even see it being related to the bloc package tbh. It's all about how you use the navigation to move between tab pages -- that is if you want to explicitly handle navigation. In case you use TabBar
then a TabController
will handle switching between tabs.
Maybe someone else can better understand your needs ๐
@RollyPeres I really can't understand why we are talking about bottom navigation. Are
_navigatorKey.currentState.popUntil((route) => route.isFirst);
_navigatorKey.currentState.pushReplacementNamed('route', arguments: arguments);
commands incorrect for bloc
? Why does this even lead to BlocListener receives the same state twice? Do you know that?
How you navigate has nothing to do with the bloc. Your first call is unneeded since you'll always be replacing the existing route thereafter.
I didn't receive two states when I was running your sample. Try a flutter clean
.
@RollyPeres
flutter clean
and just in case I tried it again now. @don-prog I'm aware you're having that issue whenever you're tapping quickly, but as already mentioned, I have tested that too and I don't get multiple states. I suggest you manually remove the app from your device + flutter clean
. ๐ค
@RollyPeres
I/flutter (19741): Elapsed milliseconds for changing tabs: 158
I/flutter (19741): _FirstPageState.initState()
I/flutter (19741): _firstPageBloc.add(FirstFirstPageEvent())
I/flutter (19741): FirstPage.buildBody()
I/flutter (19741): sl().add(FirstMainEvent())
I/flutter (19741): yield FirstState
I/flutter (19741): First page received FirstState
I/flutter (19741): First page received FirstState
Fixed it in https://github.com/don-prog/bloc_repeated_states_issue/pull/2
Your issue happened because when you're quickly switching the tabs back to first page, the new first page gets created but the previous one is not yet disposed, so the bloc listener will react twice(once from the initial first page instance and second time from the new instance).
Basically the tabs take a bit to switch so you can counter this side effect by overriding transformEvents
and debouncing a bit your event, to ensure your tabs have time to dispose the previous instance. ๐
@RollyPeres finally, thanks. I already use debounce as hot fix, but I thought that there may be some better solution.
@don-prog I don't think this has anything to do with bloc. It seems like your complaint is more about how/when tabs are disposed. You might want to bring this up as an issue on the Flutter repo if you feel it's undesirable behavior.
@felangel now I don't really think that this is bloc
issue or problem :)
Also, I ready to report this issue/undesirable behaviour to the Flutter
repo, because I think this may be so.
But I think I haven't much practical experience in the Flutter
's under the hood mechanisms, as you have. So, I also want to know your opinion about this behaviour: how incorrect is it and should I report it?
@don-prog I think this behavior is expected but that being said it's not unreasonable to open an issue and see what the Flutter team thinks.
Most helpful comment
Fixed it in https://github.com/don-prog/bloc_repeated_states_issue/pull/2
Your issue happened because when you're quickly switching the tabs back to first page, the new first page gets created but the previous one is not yet disposed, so the bloc listener will react twice(once from the initial first page instance and second time from the new instance).
Basically the tabs take a bit to switch so you can counter this side effect by overriding
transformEvents
and debouncing a bit your event, to ensure your tabs have time to dispose the previous instance. ๐