import 'dart:async'; import 'package:flutter/material.dart'; import '../theme/app_theme.dart'; /// Slide-in toast for cross-session incoming messages. class ToastOverlay extends StatefulWidget { final String sessionName; final String preview; final VoidCallback onTap; final VoidCallback onDismiss; const ToastOverlay({ super.key, required this.sessionName, required this.preview, required this.onTap, required this.onDismiss, }); @override State createState() => _ToastOverlayState(); } class _ToastOverlayState extends State with SingleTickerProviderStateMixin { late final AnimationController _controller; late final Animation _slideAnimation; Timer? _autoDismiss; @override void initState() { super.initState(); _controller = AnimationController( vsync: this, duration: const Duration(milliseconds: 300), ); _slideAnimation = Tween( begin: const Offset(0, -1), end: Offset.zero, ).animate(CurvedAnimation(parent: _controller, curve: Curves.easeOutCubic)); _controller.forward(); _autoDismiss = Timer(const Duration(seconds: 4), _dismiss); } void _dismiss() { _autoDismiss?.cancel(); _controller.reverse().then((_) { widget.onDismiss(); }); } @override void dispose() { _autoDismiss?.cancel(); _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return SlideTransition( position: _slideAnimation, child: SafeArea( bottom: false, child: GestureDetector( onTap: () { _autoDismiss?.cancel(); widget.onTap(); }, onVerticalDragEnd: (details) { if (details.primaryVelocity != null && details.primaryVelocity! < -100) { _dismiss(); } }, child: Container( margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: AppColors.darkSurface, borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( color: Colors.black.withAlpha(80), blurRadius: 12, offset: const Offset(0, 4), ), ], ), child: Row( children: [ Container( width: 36, height: 36, decoration: const BoxDecoration( color: AppColors.accent, shape: BoxShape.circle, ), child: const Icon( Icons.chat_bubble_outline, color: Colors.white, size: 18, ), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text( widget.sessionName, style: const TextStyle( color: Colors.white, fontWeight: FontWeight.w600, fontSize: 13, ), ), const SizedBox(height: 2), Text( widget.preview, style: const TextStyle( color: AppColors.darkTextSecondary, fontSize: 12, ), maxLines: 2, overflow: TextOverflow.ellipsis, ), ], ), ), ], ), ), ), ), ); } } /// Global toast manager for showing cross-session notifications. class ToastManager { ToastManager._(); static OverlayEntry? _currentEntry; static void show( BuildContext context, { required String sessionName, required String preview, required VoidCallback onTap, }) { dismiss(); _currentEntry = OverlayEntry( builder: (ctx) => Positioned( top: 0, left: 0, right: 0, child: Material( color: Colors.transparent, child: ToastOverlay( sessionName: sessionName, preview: preview, onTap: () { dismiss(); onTap(); }, onDismiss: dismiss, ), ), ), ); Overlay.of(context).insert(_currentEntry!); } static void dismiss() { _currentEntry?.remove(); _currentEntry = null; } }