simplified code across the app and consolidated duplicated helpers

This commit is contained in:
2026-09-27 17:29:54 +02:00
parent 3e856d0b1c
commit ee1240f71a
155 changed files with 1772 additions and 2546 deletions
@@ -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),
),
+8 -12
View File
@@ -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);