import 'package:flutter/material.dart'; import 'dart:async'; enum ToastType { success, error, info, warning, } class ToastService { static final GlobalKey navigatorKey = GlobalKey(); static void show({ required String message, ToastType type = ToastType.info, Duration duration = const Duration(seconds: 3), }) { final context = navigatorKey.currentState?.overlay?.context; if (context == null) return; final overlay = Overlay.of(context); OverlayEntry? overlayEntry; overlayEntry = OverlayEntry( builder: (context) => _ToastWidget( message: message, type: type, onDismiss: () { if (overlayEntry != null) { overlayEntry.remove(); } }, ), ); overlay.insert(overlayEntry); Timer(duration, () { if (overlayEntry != null && overlayEntry.mounted) { overlayEntry.remove(); } }); } } class _ToastWidget extends StatefulWidget { final String message; final ToastType type; final VoidCallback onDismiss; const _ToastWidget({ required this.message, required this.type, required this.onDismiss, }); @override State<_ToastWidget> createState() => _ToastWidgetState(); } class _ToastWidgetState extends State<_ToastWidget> with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation _animation; @override void initState() { super.initState(); _controller = AnimationController( vsync: this, duration: const Duration(milliseconds: 300), reverseDuration: const Duration(milliseconds: 300), ); _animation = Tween( begin: const Offset(0, -1.5), end: Offset.zero, ).animate(CurvedAnimation( parent: _controller, curve: Curves.easeOut, reverseCurve: Curves.easeIn, )); _controller.forward(); _startDismissTimer(); } void _startDismissTimer() { Future.delayed(const Duration(seconds: 2), () { if (mounted) { _controller.reverse().then((_) { widget.onDismiss(); }); } }); } @override void dispose() { _controller.dispose(); super.dispose(); } Map> get _toastConfig { return { ToastType.success: { 'icon': Icons.check_circle_rounded, 'color': const Color(0xFF67B547), 'gradient': const LinearGradient( colors: [Color(0xFF67B547), Color(0xFF88D86B)], begin: Alignment.centerLeft, end: Alignment.centerRight, ), }, ToastType.error: { 'icon': Icons.error_rounded, 'color': const Color(0xFFE94560), 'gradient': const LinearGradient( colors: [Color(0xFFE94560), Color(0xFFF07E3F)], begin: Alignment.centerLeft, end: Alignment.centerRight, ), }, ToastType.info: { 'icon': Icons.info_rounded, 'color': const Color(0xFF3B82F6), 'gradient': const LinearGradient( colors: [Color(0xFF3B82F6), Color(0xFF60A5FA)], begin: Alignment.centerLeft, end: Alignment.centerRight, ), }, ToastType.warning: { 'icon': Icons.warning_rounded, 'color': const Color(0xFFFACC15), 'gradient': const LinearGradient( colors: [Color(0xFFFACC15), Color(0xFFFFF748)], begin: Alignment.centerLeft, end: Alignment.centerRight, ), }, }; } @override Widget build(BuildContext context) { final config = _toastConfig[widget.type]!; final icon = config['icon'] as IconData; final gradient = config['gradient'] as LinearGradient; return Positioned( top: 50, left: 20, right: 20, child: SlideTransition( position: _animation, child: Material( color: Colors.transparent, child: Container( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 15), decoration: BoxDecoration( gradient: gradient, borderRadius: BorderRadius.circular(15), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.2), blurRadius: 15, offset: const Offset(0, 8), ), ], ), child: Row( children: [ Icon(icon, color: Colors.white, size: 28), const SizedBox(width: 15), Expanded( child: Text( widget.message, style: const TextStyle( color: Colors.white, fontSize: 16, fontWeight: FontWeight.w600, ), ), ), ], ), ), ), ), ); } }