import 'package:flutter/material.dart'; import 'package:shadcn_ui/shadcn_ui.dart'; enum DialogType { info, warning, error, success, confirm, custom, } class SuperportShadDialog extends StatelessWidget { final String title; final String? description; final Widget? content; final List? actions; final DialogType type; final bool dismissible; final double? width; final double? maxHeight; final VoidCallback? onClose; final bool showCloseButton; final bool loading; final String? loadingMessage; const SuperportShadDialog({ super.key, required this.title, this.description, this.content, this.actions, this.type = DialogType.custom, this.dismissible = true, this.width, this.maxHeight, this.onClose, this.showCloseButton = true, this.loading = false, this.loadingMessage, }); static Future show({ required BuildContext context, required String title, String? description, Widget? content, List? actions, DialogType type = DialogType.custom, bool dismissible = true, double? width, double? maxHeight, bool showCloseButton = true, }) { return showDialog( context: context, barrierDismissible: dismissible, builder: (context) => SuperportShadDialog( title: title, description: description, content: content, actions: actions, type: type, dismissible: dismissible, width: width, maxHeight: maxHeight, showCloseButton: showCloseButton, ), ); } static Future confirm({ required BuildContext context, required String title, required String message, String confirmText = '확인', String cancelText = '취소', DialogType type = DialogType.confirm, }) { return showDialog( context: context, barrierDismissible: false, builder: (context) => SuperportShadDialog( title: title, description: message, type: type, dismissible: false, showCloseButton: false, actions: [ ShadButton.outline( onPressed: () => Navigator.of(context).pop(false), child: Text(cancelText), ), ShadButton( onPressed: () => Navigator.of(context).pop(true), child: Text(confirmText), ), ], ), ); } static Future alert({ required BuildContext context, required String title, required String message, String confirmText = '확인', DialogType type = DialogType.info, }) { return showDialog( context: context, barrierDismissible: false, builder: (context) => SuperportShadDialog( title: title, description: message, type: type, dismissible: false, showCloseButton: false, actions: [ ShadButton( onPressed: () => Navigator.of(context).pop(), child: Text(confirmText), ), ], ), ); } static Future form({ required BuildContext context, required String title, required Widget form, required Future Function() onSubmit, String submitText = '저장', String cancelText = '취소', double? width, double? maxHeight, }) async { bool isLoading = false; String? errorMessage; return await showDialog( context: context, barrierDismissible: false, builder: (dialogContext) => StatefulBuilder( builder: (context, setState) { return SuperportShadDialog( title: title, width: width ?? 500, maxHeight: maxHeight, dismissible: false, showCloseButton: !isLoading, loading: isLoading, loadingMessage: '처리 중...', content: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ if (errorMessage != null) ...[ ShadAlert.destructive( title: const Text('오류'), description: Text(errorMessage!), ), const SizedBox(height: 16), ], form, ], ), actions: isLoading ? null : [ ShadButton.outline( onPressed: () => Navigator.of(dialogContext).pop(), child: Text(cancelText), ), ShadButton( onPressed: () async { setState(() { isLoading = true; errorMessage = null; }); try { final result = await onSubmit(); if (dialogContext.mounted) { Navigator.of(dialogContext).pop(result); } } catch (e) { setState(() { isLoading = false; errorMessage = e.toString(); }); } }, child: Text(submitText), ), ], ); }, ), ); } IconData _getIconForType() { switch (type) { case DialogType.info: return Icons.info_outline; case DialogType.warning: return Icons.warning_amber_outlined; case DialogType.error: return Icons.error_outline; case DialogType.success: return Icons.check_circle_outline; case DialogType.confirm: return Icons.help_outline; case DialogType.custom: default: return Icons.message_outlined; } } Color _getColorForType(ShadColorScheme colorScheme) { switch (type) { case DialogType.info: return colorScheme.primary; case DialogType.warning: return const Color(0xFFFFC107); case DialogType.error: return colorScheme.destructive; case DialogType.success: return const Color(0xFF2E8B57); case DialogType.confirm: return colorScheme.primary; case DialogType.custom: default: return colorScheme.foreground; } } @override Widget build(BuildContext context) { final theme = ShadTheme.of(context); final typeColor = _getColorForType(theme.colorScheme); return Dialog( backgroundColor: Colors.transparent, child: Container( width: width ?? 480, constraints: BoxConstraints( maxWidth: width ?? 480, maxHeight: maxHeight ?? MediaQuery.of(context).size.height * 0.8, ), decoration: BoxDecoration( color: theme.colorScheme.background, borderRadius: theme.radius, border: Border.all( color: theme.colorScheme.border, width: 1, ), ), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( border: Border( bottom: BorderSide( color: theme.colorScheme.border, width: 1, ), ), ), child: Row( children: [ if (type != DialogType.custom) ...[ Icon( _getIconForType(), color: typeColor, size: 24, ), const SizedBox(width: 12), ], Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: theme.textTheme.h4.copyWith( fontWeight: FontWeight.w600, ), ), if (description != null && content == null) ...[ const SizedBox(height: 8), Text( description!, style: theme.textTheme.p.copyWith( color: theme.colorScheme.mutedForeground, ), ), ], ], ), ), if (showCloseButton && !loading) ...[ const SizedBox(width: 12), IconButton( icon: const Icon(Icons.close, size: 20), onPressed: onClose ?? () => Navigator.of(context).pop(), style: IconButton.styleFrom( foregroundColor: theme.colorScheme.mutedForeground, ), ), ], ], ), ), if (loading) ...[ Padding( padding: const EdgeInsets.all(32), child: Column( mainAxisSize: MainAxisSize.min, children: [ const CircularProgressIndicator(), if (loadingMessage != null) ...[ const SizedBox(height: 16), Text( loadingMessage!, style: theme.textTheme.muted, ), ], ], ), ), ] else ...[ if (content != null) ...[ Flexible( child: SingleChildScrollView( padding: const EdgeInsets.all(20), child: content!, ), ), ], if (actions != null && actions!.isNotEmpty) ...[ Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( border: Border( top: BorderSide( color: theme.colorScheme.border, width: 1, ), ), ), child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ for (int i = 0; i < actions!.length; i++) ...[ if (i > 0) const SizedBox(width: 8), actions![i], ], ], ), ), ], ], ], ), ), ); } } class FormDialog extends StatefulWidget { final Widget child; final GlobalKey formKey; const FormDialog({ super.key, required this.child, required this.formKey, }); @override State createState() => _FormDialogState(); } class _FormDialogState extends State { @override Widget build(BuildContext context) { return Form( key: widget.formKey, child: widget.child, ); } }