part of '../async_action_button.dart'; typedef AsyncActionCallback = Future Function(); typedef AsyncErrorBuilder = String Function(Object error); /// Shared style for the inline error message the async-button family renders /// beneath a control after a caught failure. 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. Future runWithErrorDialog( BuildContext context, AsyncActionCallback action, { AsyncErrorBuilder? errorBuilder, }) async { try { await action(); Haptics.success(); return true; } catch (e) { Haptics.error(); if (!context.mounted) return false; InfoDialog.showError(context, e, message: errorBuilder?.call(e)); return false; } } /// Reusable busy/error state for the async-button family. Multiple buttons /// can share the same controller (e.g. a parent toolbar wanting to disable /// while any one child is running). class AsyncActionController extends ChangeNotifier { bool _busy = false; String? _error; bool get busy => _busy; String? get error => _error; Future run( AsyncActionCallback action, { AsyncErrorBuilder? errorBuilder, }) async { if (_busy) return false; _busy = true; _error = null; notifyListeners(); try { await action(); return true; } catch (e) { _error = errorBuilder != null ? errorBuilder(e) : errorToUserMessage(e); return false; } finally { _busy = false; notifyListeners(); } } void clearError() { if (_error == null) return; _error = null; notifyListeners(); } }