simplified code across the app and consolidated duplicated helpers
This commit is contained in:
@@ -32,20 +32,12 @@ class AsyncActionButton extends StatelessWidget {
|
||||
onError: onError,
|
||||
onSuccess: onSuccess,
|
||||
builder: (context, busy, handler) {
|
||||
final spinner = AppProgressIndicator.small(
|
||||
color: Theme.of(context).colorScheme.onPrimary,
|
||||
);
|
||||
final content = busy
|
||||
? Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [spinner, const SizedBox(width: 8), child],
|
||||
final lead = busy
|
||||
? AppProgressIndicator.small(
|
||||
color: Theme.of(context).colorScheme.onPrimary,
|
||||
)
|
||||
: (icon != null
|
||||
? Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [Icon(icon), const SizedBox(width: 8), child],
|
||||
)
|
||||
: child);
|
||||
: (icon == null ? null : Icon(icon));
|
||||
final content = lead == null ? child : _leadingLabel(lead, child);
|
||||
final button = ElevatedButton(
|
||||
onPressed: handler,
|
||||
style: style,
|
||||
|
||||
@@ -8,6 +8,11 @@ typedef AsyncErrorBuilder = String Function(Object error);
|
||||
TextStyle _asyncErrorTextStyle(BuildContext context) =>
|
||||
TextStyle(color: Theme.of(context).colorScheme.error, fontSize: 13);
|
||||
|
||||
Widget _leadingLabel(Widget leading, Widget label) => Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [leading, const SizedBox(width: 8), label],
|
||||
);
|
||||
|
||||
/// Wraps [action] with a try/catch that pops up an [InfoDialog] on failure
|
||||
/// (using [errorBuilder] or the default error mapper). Returns `true` on
|
||||
/// success, `false` on caught failure.
|
||||
@@ -23,14 +28,7 @@ Future<bool> runWithErrorDialog(
|
||||
} catch (e) {
|
||||
Haptics.error();
|
||||
if (!context.mounted) return false;
|
||||
final message = errorBuilder != null
|
||||
? errorBuilder(e)
|
||||
: errorToUserMessage(e);
|
||||
final details = errorToTechnicalDetails(e);
|
||||
final body = details != null && details != message
|
||||
? '$message\n\n$details'
|
||||
: message;
|
||||
InfoDialog.show(context, body, copyable: true, title: 'Fehler');
|
||||
InfoDialog.showError(context, e, message: errorBuilder?.call(e));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -55,14 +53,13 @@ class AsyncActionController extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
try {
|
||||
await action();
|
||||
_busy = false;
|
||||
notifyListeners();
|
||||
return true;
|
||||
} catch (e) {
|
||||
_busy = false;
|
||||
_error = errorBuilder != null ? errorBuilder(e) : errorToUserMessage(e);
|
||||
notifyListeners();
|
||||
return false;
|
||||
} finally {
|
||||
_busy = false;
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -68,15 +68,11 @@ class _AsyncDialogActionState extends State<AsyncDialogAction> {
|
||||
}
|
||||
},
|
||||
child: _controller.busy
|
||||
? Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
AppProgressIndicator.small(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(widget.confirmLabel),
|
||||
],
|
||||
? _leadingLabel(
|
||||
AppProgressIndicator.small(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
Text(widget.confirmLabel),
|
||||
)
|
||||
: Text(widget.confirmLabel),
|
||||
),
|
||||
|
||||
@@ -23,15 +23,13 @@ class _AsyncMixin extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _AsyncMixinState extends State<_AsyncMixin> {
|
||||
late final AsyncActionController _internal;
|
||||
AsyncActionController get _controller => widget.controller ?? _internal;
|
||||
AsyncActionController? _own;
|
||||
AsyncActionController get _controller =>
|
||||
widget.controller ?? (_own ??= AsyncActionController());
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (widget.controller == null) {
|
||||
_internal = AsyncActionController();
|
||||
}
|
||||
_controller.addListener(_onControllerChange);
|
||||
}
|
||||
|
||||
@@ -39,7 +37,7 @@ class _AsyncMixinState extends State<_AsyncMixin> {
|
||||
void didUpdateWidget(covariant _AsyncMixin oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.controller != widget.controller) {
|
||||
(oldWidget.controller ?? _internal).removeListener(_onControllerChange);
|
||||
(oldWidget.controller ?? _own)?.removeListener(_onControllerChange);
|
||||
_controller.addListener(_onControllerChange);
|
||||
}
|
||||
}
|
||||
@@ -47,9 +45,7 @@ class _AsyncMixinState extends State<_AsyncMixin> {
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.removeListener(_onControllerChange);
|
||||
if (widget.controller == null) {
|
||||
_internal.dispose();
|
||||
}
|
||||
_own?.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -76,11 +72,11 @@ class _AsyncMixinState extends State<_AsyncMixin> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final handler = widget.onPressed == null ? null : _trigger;
|
||||
final busy = _controller.busy;
|
||||
return widget.builder(
|
||||
context,
|
||||
_controller.busy,
|
||||
_controller.busy ? null : handler,
|
||||
busy,
|
||||
busy || widget.onPressed == null ? null : _trigger,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,15 +29,11 @@ class AsyncTextButton extends StatelessWidget {
|
||||
onSuccess: onSuccess,
|
||||
builder: (context, busy, handler) {
|
||||
final content = busy
|
||||
? Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
AppProgressIndicator.small(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
child,
|
||||
],
|
||||
? _leadingLabel(
|
||||
AppProgressIndicator.small(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
child,
|
||||
)
|
||||
: child;
|
||||
final button = TextButton(onPressed: handler, child: content);
|
||||
|
||||
Reference in New Issue
Block a user