initial
This commit is contained in:
75
lib/utils/async_mutation.dart
Normal file
75
lib/utils/async_mutation.dart
Normal file
@@ -0,0 +1,75 @@
|
||||
// ignore_for_file: unreachable_switch_case
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'async_mutation.freezed.dart';
|
||||
|
||||
// TODO: test and improve
|
||||
|
||||
@freezed
|
||||
class AsyncMutation with _$AsyncMutation {
|
||||
const AsyncMutation._();
|
||||
|
||||
const factory AsyncMutation.idle() = Idle;
|
||||
const factory AsyncMutation.inProgress() = InProgress;
|
||||
const factory AsyncMutation.fail(Object error, StackTrace stackTrace) = Fail;
|
||||
const factory AsyncMutation.success() = Success;
|
||||
|
||||
bool get isInProgress => this is InProgress;
|
||||
}
|
||||
|
||||
/// temporary(and hacky) way to manage async mutations
|
||||
({
|
||||
AsyncMutation state,
|
||||
ValueChanged<Future<T>> setFuture,
|
||||
ValueChanged<void Function(Object error)> setOnFailure,
|
||||
}) useMutation<T>({
|
||||
void Function(Object error)? initialOnFailure,
|
||||
void Function()? initialOnSuccess,
|
||||
}) {
|
||||
final mutationUpdate = useState<Future<T>?>(null);
|
||||
final mutationState = useFuture(mutationUpdate.value);
|
||||
final failureCallBack =
|
||||
useValueNotifier<void Function(Object error)?>(initialOnFailure);
|
||||
final successCallBack = useValueNotifier<void Function()?>(initialOnSuccess);
|
||||
|
||||
// map AsyncSnapshot to AsyncMutation which is easier to consume
|
||||
final mapped = useMemoized(
|
||||
() => switch (mutationState) {
|
||||
// ignore: unused_local_variable
|
||||
AsyncSnapshot(:final data?) => const Success(),
|
||||
AsyncSnapshot(connectionState: ConnectionState.waiting) =>
|
||||
const InProgress(),
|
||||
AsyncSnapshot(:final error?, :final stackTrace?) =>
|
||||
Fail(error, stackTrace),
|
||||
_ => const Idle(),
|
||||
},
|
||||
[mutationState],
|
||||
);
|
||||
|
||||
// one-of callback in failure
|
||||
useMemoized(
|
||||
() {
|
||||
if (mapped case Fail(:final error)) {
|
||||
// if callback tries to build widget(show snackbar for example) this will prevent exceptions
|
||||
WidgetsBinding.instance.addPostFrameCallback(
|
||||
(_) => failureCallBack.value?.call(error),
|
||||
);
|
||||
}
|
||||
if (mapped case Success()) {
|
||||
WidgetsBinding.instance.addPostFrameCallback(
|
||||
(_) => successCallBack.value?.call(),
|
||||
);
|
||||
}
|
||||
},
|
||||
[mapped, failureCallBack.value, successCallBack.value],
|
||||
);
|
||||
|
||||
return (
|
||||
state: mapped,
|
||||
setFuture: (future) => mutationUpdate.value = future,
|
||||
setOnFailure: (onFailure) => failureCallBack.value = onFailure,
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user