Thank you for your nice package for using comfortable BLoC pattern.
However, I'm suffering from "STATE RESIDUE" of BlocBuilder. For example, I pushed another route page when state is specific condition, but it infinitely push route when I pop that route. It is the because of state residue. How can I clean or eliminate state of BlocBuilder?
Here is my code and BlocNavigator
is my custom navigator using WidgetsBinding
class.
if(state.isMovieCrawlSucceeded) {
BlocNavigator.push(context,
MaterialPageRoute(builder: (_)=>MovieScreen(movie: state.clickedMovie)));
}
Thank you.
Hi @baeharam 馃憢
Great question! You can dispatch an event to reset the state before navigating. Let me know if that helps 馃憤
@felangel Why don't we allow to reset via dispose()
method too? Such as
@override
void dispose() {
_loginBloc.dispose(resetState: true);
super.dispose();
}
@felangel Thanks for your quick response! 馃槃 Firstly, I wrote that kind of event like StateClearEvent()
. However, then do I have to always dispatch this event to clean state residue? Is it weak point of BLoC or my fault?
@baeharam yes you would need to in order to clear the state. When possible I鈥檇 avoid using a bloc for navigation because it is not business logic and is flutter specific code.
I think there can probably be improvements made to the flutter_bloc library to make this a bit easier but I need to spend some time to figure out the right way to support this.
@anticafe we can do that but then dispose is doing more than one thing and I鈥檇 prefer to keep the API as simple/predictable as possible. Also in this case I don鈥檛 think you鈥檇 want to dispose the bloc when you navigate because when you pop and come back to the current view you will probably still want the bloc to function properly. Thoughts?
@felangel I'm agree with you in most case (back-able pages) we don't need to reset state of bloc because that bloc would be reused in another pages. However we still need it for some non-back-able pages. For example:
According to @baeharam 's above comment, in step (1) should we invoke _loginBloc.dispatch(LoginUninitializedState())
when dispose()
? If yes, then I hope you can provide a method to dispatch bloc with initial state, such as _loginBloc.resetState()
instead _loginBloc.displatch(_loginBloc.initialState)
which is longer.
@felangel Ok, I'll expect your awesome works!
@anticafe Thanks for your advice.
@anticafe thanks for providing more details!
In the example you gave I wouldn鈥檛 expect LoginBloc to be a global bloc and I鈥檇 expect it to be disposed and deallocated when you navigate to the HomePage. Then when you logout your LoginPage should create a brand new LoginBloc which would have an initialState. Does that help?
Closing this for now but feel free to comment with more questions or details and I鈥檓 happy to continue the conversation 馃憤
@felangel
I had the same problem, and as you said, creating a local bloc solves it.
But, by this way creates another problem, I dont find a way to mock the bloc in widget unit tests.
So, how can I do it?
@Paitomax in that case you can create a container widget that just serves as an injector for the actual widget like:
class FeatureAScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => FeatureABloc(),
child: FeatureA(),
);
}
}
class FeatureA extends StatelessWidget { ... }
Then you can:
FeatureAScreen
renders FeatureA
FeatureA
by providing a mock bloc implementationtestWidgets('...', (tester) async {
final featureABloc = MockFeatureABloc();
when(featureABloc.state).thenReturn(SomeState());
await tester.pumpWidget(
BlocProvider.value(
value: featureABloc,
child: FeatureA(),
),
);
// expect stuff
});
Hope that helps 馃憤
@felangel That works, thx!
Most helpful comment
@felangel That works, thx!