import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:on_field_work/helpers/widgets/my_text.dart'; import 'package:on_field_work/helpers/widgets/my_snackbar.dart'; class ConfirmDialog extends StatelessWidget { final String title; final String message; /// Text for confirm button (default: "Delete") final String confirmText; /// Text for cancel button (default: "Cancel") final String cancelText; /// Icon shown in the dialog header (default: Icons.delete) final IconData icon; /// Icon for confirm button (default: Icons.delete_forever) final IconData confirmIcon; /// Icon for cancel button (default: Icons.close) final IconData cancelIcon; /// Background color for confirm button (default: Colors.redAccent) final Color confirmColor; /// Callback fired when confirm is pressed and awaited. final Future Function() onConfirm; /// External RxBool to observe the loading state, if null internal is used final RxBool? isProcessing; /// Custom error message shown in snackbar if confirmation fails final String errorMessage; /// Text shown in confirm button while loading final String loadingText; const ConfirmDialog({ super.key, required this.title, required this.message, required this.onConfirm, this.confirmText = "Delete", this.cancelText = "Cancel", this.icon = Icons.delete, this.confirmIcon = Icons.delete_forever, this.cancelIcon = Icons.close, this.confirmColor = Colors.redAccent, this.isProcessing, this.errorMessage = "Failed to complete action. Try again.", this.loadingText = "Submitting…", }); @override Widget build(BuildContext context) { final RxBool loading = isProcessing ?? false.obs; final theme = Theme.of(context); return Dialog( backgroundColor: theme.colorScheme.surface, insetPadding: const EdgeInsets.symmetric(horizontal: 24, vertical: 24), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)), child: ConstrainedBox( constraints: const BoxConstraints( minWidth: 280, maxWidth: 480, ), child: Padding( padding: const EdgeInsets.fromLTRB(24, 20, 24, 16), child: _ContentView( title: title, message: message, icon: icon, confirmColor: confirmColor, confirmText: confirmText, cancelText: cancelText, confirmIcon: confirmIcon, cancelIcon: cancelIcon, loading: loading, onConfirm: onConfirm, errorMessage: errorMessage, loadingText: loadingText, ), ), ), ); } } class _ContentView extends StatelessWidget { final String title, message, confirmText, cancelText, loadingText; final IconData icon, confirmIcon, cancelIcon; final Color confirmColor; final RxBool loading; final Future Function() onConfirm; final String errorMessage; const _ContentView({ required this.title, required this.message, required this.icon, required this.confirmColor, required this.confirmText, required this.cancelText, required this.confirmIcon, required this.cancelIcon, required this.loading, required this.onConfirm, required this.errorMessage, required this.loadingText, }); @override Widget build(BuildContext context) { final theme = Theme.of(context); final colorScheme = theme.colorScheme; return Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( decoration: BoxDecoration( color: confirmColor.withOpacity(0.12), shape: BoxShape.circle, ), padding: const EdgeInsets.all(10), child: Icon( icon, size: 22, color: confirmColor, ), ), const SizedBox(width: 12), Expanded( child: MyText.titleLarge( title, fontWeight: 700, color: colorScheme.onSurface, ), ), ], ), const SizedBox(height: 12), MyText.bodyMedium( message, textAlign: TextAlign.left, color: colorScheme.onSurface.withValues(alpha: 0.75), ), const SizedBox(height: 20), Divider( height: 1, color: colorScheme.outlineVariant.withValues(alpha: 0.6), ), const SizedBox(height: 12), Align( alignment: Alignment.center, child: SizedBox( width: double.infinity, // allow full available width child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Expanded( child: Obx( () => _DialogButton( text: cancelText, icon: cancelIcon, color: Colors.transparent, textColor: colorScheme.onSurface, isFilled: false, isLoading: false, onPressed: loading.value ? null : () => Navigator.pop(context, false), ), ), ), const SizedBox(width: 20), Expanded( child: Obx( () => _DialogButton( text: loading.value ? loadingText : confirmText, icon: confirmIcon, color: confirmColor, textColor: Colors.white, isFilled: true, isLoading: loading.value, onPressed: () async { try { loading.value = true; await onConfirm(); Navigator.pop(context, true); } catch (e) { showAppSnackbar( title: "Error", message: errorMessage, type: SnackbarType.error, ); } finally { loading.value = false; } }, ), ), ), ], ), ), ), ], ); } } class _DialogButton extends StatelessWidget { final String text; final IconData icon; final Color color; final Color textColor; final bool isFilled; final VoidCallback? onPressed; final bool isLoading; const _DialogButton({ required this.text, required this.icon, required this.color, required this.textColor, required this.isFilled, required this.onPressed, this.isLoading = false, }); @override Widget build(BuildContext context) { final theme = Theme.of(context); final ButtonStyle style = isFilled ? ElevatedButton.styleFrom( backgroundColor: color, foregroundColor: textColor, elevation: 0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(999), ), padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 10), ) : OutlinedButton.styleFrom( foregroundColor: textColor, side: BorderSide( color: theme.colorScheme.outline.withValues(alpha: 0.7), ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(999), ), padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), ); final Widget iconWidget = isLoading ? SizedBox( width: 18, height: 18, child: CircularProgressIndicator( strokeWidth: 2, color: isFilled ? Colors.white : theme.colorScheme.primary, ), ) : Icon(icon, size: 18); final Widget labelWidget = MyText.bodyMedium( text, color: isFilled ? Colors.white : textColor, fontWeight: 600, ); final child = Row( mainAxisSize: MainAxisSize.min, children: [ iconWidget, const SizedBox(width: 8), Flexible(child: labelWidget), ], ); return isFilled ? ElevatedButton( onPressed: isLoading ? null : onPressed, style: style, child: child, ) : OutlinedButton( onPressed: isLoading ? null : onPressed, style: style, child: child, ); } }