I want to use a method of cubit in many part of application, and want to show snackbar on errors, How can I remove duplication code of showing snackbar?
If I use BlocListener I must write showing snackbar multiple times, ?Is it a good practice that I pass context to method of cubit and show snackbar in cubit method itself?
Hi @sm2017 馃憢
Thanks for opening an issue!
I would advise against injecting BuildContext into your blocs and cubits because they should be decoupled from the presentation layer and also BuildContext shouldn't be cached because it can quickly become stale.
If I use BlocListener I must write showing snackbar multiple times
Can you describe why? You should be able to write the SnackBar code once and you can use the new ScaffoldMessenger API.
Let me know if that helps 馃憤
@felangel let me show my problem with a minimal code
class MyCubit extends Cubit<MyState> {
final MyRepository myRepository;
MyCubit({
@required MyRepository this.myRepository,
}) : super(MyState());
Future<void> doSomething() => myRepository.doSomething();
}
In several part of application I call doSomething and to show error to user I must re-write the following snippet
try{
await context.read<MyCubit>().doSomething();
} on ApiException catch (e) {
// Show `e.message` with snackbar
} catch (e) {
// Show "Something went wrong" with snackbar and rethrow
rethrow;
}
Here ApiException is my custom Exception and its message is a human readable message, I always has this logic, and want to write it once, in javascript and react-ecosystem, I have a bool autoErrorReport, when it's true , it automatically show errors with snackbar and it is not needed to manually catch errors
Note: it is not important but I use flushbar to show snackbar
Hi @sm2017 馃憢
You should never inject context into your bloc or do the try catch in the UI.
Instead in your bloc you should:
enum Failure { api }
Future<void> doSomething() async {
try{
await myRepository.doSomething();
} on ApiException {
emit(MyStateFailure(failure: Failure.api));
}
// you should never need a generic `catch` here because it's up to you to handle all the possibilities in your repo and then you'll
// know all your potential exceptions at this point.
}
You can also create a reusable listener if you need to listen to these errors in multiple places:
class FailureListener extends StatelessWidget {
const FailureListener({
Key key,
this.child,
}) : super(key: key);
final Widget child;
@override
Widget build(BuildContext context) {
return BlocListener<MyCubit, MyState>(
listener: (context, state) {
if (state is MyStateFailure) {
if (state.failure == Failure.api) {
// show snackBar for this type of failure
}
}
},
child: child,
);
}
}
And just nest this listener wherever you need it. This is just some basic example which can obviously be improved, I'm just trying to make a point here.
@narcodico Thanks a lot!!
@sm2017 See the updated answer for a cleaner approach. This can be simplified further down by using some extension methods and redirected constructors but I wanted to eliminate the need of explicitly catching the error which shouldn't be necessary in bloc.
Most helpful comment
@sm2017 See the updated answer for a cleaner approach. This can be simplified further down by using some extension methods and redirected constructors but I wanted to eliminate the need of explicitly catching the error which shouldn't be necessary in bloc.