import 'package:flutter/material.dart'; import '../core/theme/app_theme.dart'; enum NotificationType { success, error, warning, info } class DialogService { // Singleton pattern static final DialogService _instance = DialogService._internal(); factory DialogService() { return _instance; } DialogService._internal(); // Global keys final GlobalKey scaffoldMessengerKey = GlobalKey(); final GlobalKey navigatorKey = GlobalKey(); // Helper method to get context (use with caution, better to pass context or use keys) BuildContext? get _context => navigatorKey.currentContext; // --- SnackBar Notifications --- void showSnackBar({ required String message, NotificationType type = NotificationType.info, Duration duration = const Duration(seconds: 3), String? actionLabel, VoidCallback? onAction, }) { if (scaffoldMessengerKey.currentState == null) { debugPrint( '⚠️ ScaffoldMessengerState is null, cannot show SnackBar: $message', ); return; } Color backgroundColor; Color textColor = Colors.white; IconData icon; switch (type) { case NotificationType.success: backgroundColor = AppTheme.success; icon = Icons.check_circle_outline; break; case NotificationType.error: backgroundColor = AppTheme.error.withOpacity(0.9); icon = Icons.error_outline; break; case NotificationType.warning: backgroundColor = AppTheme.warning; textColor = Colors.black87; // Dark text for yellow background icon = Icons.warning_amber_rounded; break; case NotificationType.info: backgroundColor = AppTheme.primaryBrown; icon = Icons.info_outline; break; } scaffoldMessengerKey.currentState!.removeCurrentSnackBar(); scaffoldMessengerKey.currentState!.showSnackBar( SnackBar( content: Row( children: [ Icon(icon, color: textColor, size: 24), const SizedBox(width: 12), Expanded( child: Text( message, style: TextStyle( color: textColor, fontSize: 14, fontWeight: FontWeight.w500, fontFamily: 'Poppins', ), ), ), ], ), backgroundColor: backgroundColor, duration: duration, behavior: SnackBarBehavior.floating, margin: const EdgeInsets.all(16), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), action: actionLabel != null ? SnackBarAction( label: actionLabel, textColor: textColor, onPressed: onAction ?? () {}, ) : null, ), ); } void showSuccess(String message) { showSnackBar(message: message, type: NotificationType.success); } void showError(String message) { showSnackBar(message: message, type: NotificationType.error); } void showWarning(String message) { showSnackBar(message: message, type: NotificationType.warning); } void showInfo(String message) { showSnackBar(message: message, type: NotificationType.info); } // --- Dialogs --- Future showAlertDialog({ required String title, required String message, String buttonText = 'OK', BuildContext? context, }) async { final ctx = context ?? _context; if (ctx == null) return; return showDialog( context: ctx, builder: (BuildContext context) { return AlertDialog( title: Text( title, style: const TextStyle(fontWeight: FontWeight.bold), ), content: Text(message), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), actions: [ TextButton( child: Text(buttonText), onPressed: () { Navigator.of(context).pop(); }, ), ], ); }, ); } Future showConfirmDialog({ required String title, required String message, String confirmText = 'Ya', String cancelText = 'Batal', bool isDanger = false, BuildContext? context, }) async { final ctx = context ?? _context; if (ctx == null) return false; return await showDialog( context: ctx, builder: (BuildContext context) { return AlertDialog( title: Text( title, style: const TextStyle(fontWeight: FontWeight.bold), ), content: Text(message), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), actions: [ TextButton( child: Text( cancelText, style: const TextStyle(color: Colors.grey), ), onPressed: () { Navigator.of(context).pop(false); }, ), TextButton( child: Text( confirmText, style: TextStyle( color: isDanger ? AppTheme.error : AppTheme.primaryBrown, fontWeight: FontWeight.bold, ), ), onPressed: () { Navigator.of(context).pop(true); }, ), ], ); }, ) ?? false; } Future showLoadingDialog({ String message = 'Memuat...', BuildContext? context, }) async { final ctx = context ?? _context; if (ctx == null) return; showDialog( context: ctx, barrierDismissible: false, builder: (BuildContext context) { return PopScope( canPop: false, // Prevent back button child: Dialog( backgroundColor: Colors.transparent, elevation: 0, child: Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), ), child: Column( mainAxisSize: MainAxisSize.min, children: [ const CircularProgressIndicator(color: AppTheme.primaryBrown), const SizedBox(height: 20), Text( message, style: const TextStyle( fontFamily: 'Poppins', fontSize: 16, fontWeight: FontWeight.w500, ), ), ], ), ), ), ); }, ); } void hideLoadingDialog({BuildContext? context}) { final ctx = context ?? _context; if (ctx == null) { // If we don't have context but assume the navigator is attached if (navigatorKey.currentState?.canPop() ?? false) { navigatorKey.currentState?.pop(); } return; } Navigator.of(ctx).pop(); } }