import 'package:flutter/material.dart'; import 'dart:ui'; /// Dialog de résultat centré avec animation /// Affiche un résultat de succès ou d'erreur de manière élégante class ResultDialog extends StatefulWidget { final bool success; final String message; final Duration? autoDismiss; const ResultDialog({ super.key, required this.success, required this.message, this.autoDismiss, }); /// Affiche un dialog de résultat centré /// /// [success] : true pour succès, false pour erreur /// [message] : Message à afficher /// [autoDismiss] : Durée avant fermeture automatique (optionnel, uniquement pour succès) static Future show({ required BuildContext context, required bool success, required String message, Duration? autoDismiss, }) async { return showDialog( context: context, barrierDismissible: false, barrierColor: Colors.black54, builder: (context) => ResultDialog( success: success, message: message, autoDismiss: success ? (autoDismiss ?? const Duration(seconds: 2)) : null, ), ); } @override State createState() => _ResultDialogState(); } class _ResultDialogState extends State with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation _scaleAnimation; late Animation _fadeAnimation; @override void initState() { super.initState(); _controller = AnimationController( vsync: this, duration: const Duration(milliseconds: 400), ); _scaleAnimation = CurvedAnimation( parent: _controller, curve: Curves.elasticOut, ); _fadeAnimation = Tween( begin: 0.0, end: 1.0, ).animate(CurvedAnimation( parent: _controller, curve: const Interval(0.0, 0.5, curve: Curves.easeOut), )); _controller.forward(); // Auto-fermeture si demandé if (widget.autoDismiss != null) { Future.delayed(widget.autoDismiss!, () { if (mounted) { Navigator.of(context).pop(); } }); } } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return FadeTransition( opacity: _fadeAnimation, child: BackdropFilter( filter: ImageFilter.blur( sigmaX: 8.0, sigmaY: 8.0, ), child: Dialog( backgroundColor: Colors.transparent, elevation: 0, child: ScaleTransition( scale: _scaleAnimation, child: _buildContent(context), ), ), ), ); } Widget _buildContent(BuildContext context) { final theme = Theme.of(context); final iconColor = widget.success ? Colors.green : Colors.red; final icon = widget.success ? Icons.check_circle : Icons.error; return Container( constraints: const BoxConstraints( maxWidth: 340, ), padding: const EdgeInsets.all(32), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(20), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.2), blurRadius: 24, spreadRadius: 4, offset: const Offset(0, 10), ), ], ), child: Column( mainAxisSize: MainAxisSize.min, children: [ // Icône principale Container( width: 80, height: 80, decoration: BoxDecoration( color: iconColor.withOpacity(0.1), shape: BoxShape.circle, ), child: Icon( icon, size: 50, color: iconColor, ), ), const SizedBox(height: 24), // Message Text( widget.message, style: theme.textTheme.titleMedium?.copyWith( fontSize: 17, fontWeight: FontWeight.w500, color: Colors.grey[800], height: 1.4, ), textAlign: TextAlign.center, ), // Bouton OK pour les erreurs if (!widget.success) ...[ const SizedBox(height: 28), SizedBox( width: double.infinity, child: ElevatedButton( onPressed: () => Navigator.of(context).pop(), style: ElevatedButton.styleFrom( backgroundColor: iconColor, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 14), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), elevation: 0, ), child: const Text( 'OK', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, letterSpacing: 0.5, ), ), ), ), ], ], ), ); } }