Bloc: Less boilerplate code

Created on 23 Nov 2020  路  5Comments  路  Source: felangel/bloc

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?

question

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.

All 5 comments

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.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

krusek picture krusek  路  3Comments

shawnchan2014 picture shawnchan2014  路  3Comments

hivesey picture hivesey  路  3Comments

rsnider19 picture rsnider19  路  3Comments

MahdiPishguy picture MahdiPishguy  路  3Comments