Files
Client/lib/widget/async_actions/async_mixin.dart
T

122 lines
3.1 KiB
Dart

part of '../async_action_button.dart';
class _AsyncMixin extends StatefulWidget {
final AsyncActionCallback? onPressed;
final AsyncActionController? controller;
final AsyncErrorBuilder? errorBuilder;
final void Function(String message)? onError;
final VoidCallback? onSuccess;
final bool showInlineError;
final Widget Function(BuildContext context, bool busy, VoidCallback? handler)
builder;
const _AsyncMixin({
required this.onPressed,
required this.builder,
this.controller,
this.errorBuilder,
this.onError,
this.onSuccess,
this.showInlineError = false,
});
@override
State<_AsyncMixin> createState() => _AsyncMixinState();
}
class _AsyncMixinState extends State<_AsyncMixin> {
AsyncActionController? _own;
AsyncActionController get _controller =>
widget.controller ?? (_own ??= AsyncActionController());
@override
void initState() {
super.initState();
_controller.addListener(_onControllerChange);
}
@override
void didUpdateWidget(covariant _AsyncMixin oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.controller != widget.controller) {
(oldWidget.controller ?? _own)?.removeListener(_onControllerChange);
_controller.addListener(_onControllerChange);
}
}
@override
void dispose() {
_controller.removeListener(_onControllerChange);
_own?.dispose();
super.dispose();
}
void _onControllerChange() {
if (mounted) setState(() {});
}
Future<void> _trigger() async {
final action = widget.onPressed;
if (action == null) return;
final success = await _controller.run(
action,
errorBuilder: widget.errorBuilder,
);
if (!mounted) return;
if (success) {
Haptics.success();
widget.onSuccess?.call();
} else if (_controller.error != null) {
Haptics.error();
widget.onError?.call(_controller.error!);
}
}
@override
Widget build(BuildContext context) {
final busy = _controller.busy;
final child = widget.builder(
context,
busy,
busy || widget.onPressed == null ? null : _trigger,
);
if (!widget.showInlineError) return child;
return _InlineErrorWrapper(controller: _controller, child: child);
}
}
class _InlineErrorWrapper extends StatelessWidget {
final AsyncActionController controller;
final Widget child;
const _InlineErrorWrapper({required this.controller, required this.child});
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: controller,
builder: (context, _) {
final err = controller.error;
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
child,
if (err != null) ...[
const SizedBox(height: 8),
Semantics(
liveRegion: true,
container: true,
child: Text(
err,
textAlign: TextAlign.center,
style: _asyncErrorTextStyle(context),
),
),
],
],
);
},
);
}
}