guard<T> static method
Transforms a Future that may fail into something that is safe to read.
This is useful to avoid having to do a tedious try/catch
. Instead of
writing:
class MyNotifier extends AsyncNotifier<MyData> {
@override
Future<MyData> build() => Future.value(MyData());
Future<void> sideEffect() async {
state = const AsyncValue.loading();
try {
final response = await dio.get('my_api/data');
final data = MyData.fromJson(response);
state = AsyncValue.data(data);
} catch (err, stack) {
state = AsyncValue.error(err, stack);
}
}
}
We can use guard to simplify it:
class MyNotifier extends AsyncNotifier<MyData> {
@override
Future<MyData> build() => Future.value(MyData());
Future<void> sideEffect() async {
state = const AsyncValue.loading();
// does the try/catch for us like previously
state = await AsyncValue.guard(() async {
final response = await dio.get('my_api/data');
return Data.fromJson(response);
});
}
}
An optional callback can be specified to catch errors only under a certain condition. In the following example, we catch all exceptions beside FormatExceptions.
AsyncValue.guard(
() async { /* ... */ },
// Catch all errors beside [FormatException]s.
(err) => err is! FormatException,
);
}
Implementation
static Future<AsyncValue<T>> guard<T>(
Future<T> Function() future, [
bool Function(Object)? test,
]) async {
try {
return AsyncValue.data(await future());
} catch (err, stack) {
if (test == null) {
return AsyncValue.error(err, stack);
}
if (test(err)) {
return AsyncValue.error(err, stack);
}
Error.throwWithStackTrace(err, stack);
}
}