Failed actions
Actions that fail can simply throw errors using throw SomeError()
.
Throwing an error interrupts the execution of the action reducer, meaning it will not return a new state, and will not modify the store state.
Async Redux will also automatically catch those errors and deal with them globally, depending on the error type. We will see later how to set up this general error handling, but for the moment let's see how to deal with only a specific type of error called a user exception.
User exceptions
An UserException
is a special type of error, provided natively by Async Redux.
It's meant to be shown to the user, and not to be considered a bug in the code.
In other words, if something wrong happens in an action, and it's something that the user can fix,
you can throw UserException('Some error message')
with a suitable error message.
For example, if a user is transferring money and the amount is zero,
you don't want to log this as an error, but you want to show a message to the user
saying "You can't transfer zero money":
class TransferMoneyAction extends AppAction {
final double amount;
TransferMoneyAction(this.amount);
Future<AppState> reduce() async {
if (amount == 0) throw UserException('You can't transfer zero money.');
else return state.copy(cashBalance: state.cashBalance - amount);
}
}
As another example, suppose you want to save the user's name, and you only accept names with at least 4 characters:
class SaveUserAction extends ReduxAction<AppState> {
final String name;
SaveUserAction(this.name);
Future<AppState> reduce() async {
if (name.length < 4) throw UserException('Name must have at least 4 letters.'));
else await saveUser(name);
return null;
}
}
Error queue
When an action throws a UserException
, it is put automatically into a special error queue
in the store, from where it may be shown to the user by an "error widget" that consumes the errors,
one by one.
The error widget should be set up by your Team Lead once, globally.
All that the regular developers have to do in their day-to-day work is throw UserException
s and
be done with it.
What the error widget looks like is up to you. It can be a dialog that opens with the error message, it can be a toast, a list of errors displayed somewhere, or something else entirely.
Providing your own widget to consume the error queue is an advanced topic that will be covered later, but for now, let's see how to use the built-in error dialog that's provided out of the box by Async Redux.
Showing error messages in a dialog
To show error messages to the user, make your actions throw a UserException
, and then wrap your
home-page with UserExceptionDialog
, below StoreProvider
and MaterialApp
:
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return StoreProvider<AppState>(
store: store,
child: MaterialApp(
navigatorKey: navigatorKey,
home: UserExceptionDialog<AppState>(
child: MyHomePage(),
)));
}
}
Note: If you are not using the
home
parameter of theMaterialApp
widget, you can also put theUserExceptionDialog
thebuilder
parameter. Please note, if you do that you must define theNavigateAction.navigatorKey
of the Navigator. Please, see the documentation of theUserExceptionDialog.useLocalContext
parameter for more information.
Note: In the
Store
constructor you can set the maximum number of errors that queue can hold.
Changing the default error dialog
As explained in the beginning of this section, if you use the build-in error handling you must wrap
your home-page with UserExceptionDialog
. There, you may pass the onShowUserExceptionDialog
parameter to change the default dialog, show a toast, or some other suitable widget:
UserExceptionDialog<AppState>(
onShowUserExceptionDialog:
(BuildContext context, UserException userException) => showDialog(...),
child: MyHomePage(),
);
The UserExceptionDialog
can display any error widget you want in front of all the others
on the screen. If this is not what you want, you can easily create your
own MyUserExceptionWidget
to intercept the errors and do whatever you want. Start by
copying user_exception_dialog.dart
(which contains UserExceptionDialog
and its _ViewModel
)
into another file, and search for the didUpdateWidget
method. This method will be called each
time an error is available, and there you can record this information in the widget's own state.
You can then change the screen in any way you want, according to that saved state, in this
widget's build
method.
Try running the: Show Error Dialog Example.
Next, let's see how failed actions can also be shown as error messages inside your widgets.