import 'package:flutter/material.dart'; import 'package:qrscanner/models/auth_session.dart'; import 'package:qrscanner/screens/login_screen.dart'; import 'package:qrscanner/screens/scanner_screen.dart'; import 'package:qrscanner/services/auth_service.dart'; import 'package:qrscanner/theme/app_theme.dart'; class HomeScreen extends StatelessWidget { const HomeScreen({ super.key, required this.session, }); final AuthSession session; Future _logout(BuildContext context) async { await AuthService().clearSession(); if (!context.mounted) return; Navigator.of(context).pushReplacement( PageRouteBuilder( pageBuilder: (context, animation, secondaryAnimation) => const LoginScreen(), transitionsBuilder: (context, animation, secondaryAnimation, child) { return FadeTransition(opacity: animation, child: child); }, transitionDuration: const Duration(milliseconds: 400), ), ); } void _openScanner(BuildContext context) { Navigator.of(context).push( PageRouteBuilder( pageBuilder: (context, animation, secondaryAnimation) => ScannerScreen(session: session), transitionsBuilder: (context, animation, secondaryAnimation, child) { return FadeTransition( opacity: animation, child: SlideTransition( position: Tween( begin: const Offset(0, 0.06), end: Offset.zero, ).animate(CurvedAnimation( parent: animation, curve: Curves.easeOutCubic, )), child: child, ), ); }, transitionDuration: const Duration(milliseconds: 380), ), ); } @override Widget build(BuildContext context) { final expiresHours = (session.expiresIn / 3600).round(); return Scaffold( body: Stack( children: [ Container( decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ AppTheme.backgroundDark, Color(0xFF0D1B2A), Color(0xFF12182B), ], ), ), ), Positioned( top: -60, right: -40, child: Container( width: 220, height: 220, decoration: BoxDecoration( shape: BoxShape.circle, gradient: RadialGradient( colors: [ AppTheme.primary.withValues(alpha: 0.12), Colors.transparent, ], ), ), ), ), SafeArea( child: Column( children: [ Padding( padding: const EdgeInsets.fromLTRB(8, 8, 8, 0), child: Row( children: [ const Spacer(), IconButton( icon: const Icon(Icons.logout_rounded), tooltip: 'Cerrar sesión', onPressed: () => _logout(context), ), ], ), ), Expanded( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 32), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( width: 110, height: 110, decoration: BoxDecoration( borderRadius: BorderRadius.circular(28), gradient: const LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [AppTheme.primary, AppTheme.primaryDark], ), boxShadow: [ BoxShadow( color: AppTheme.primary.withValues(alpha: 0.35), blurRadius: 28, offset: const Offset(0, 10), ), ], ), child: const Icon( Icons.qr_code_scanner_rounded, size: 52, color: Colors.white, ), ), const SizedBox(height: 28), Text( 'Hola, ${session.username}', style: Theme.of(context).textTheme.headlineSmall?.copyWith( fontWeight: FontWeight.bold, ), ), const SizedBox(height: 10), Text( 'Listo para validar pases', style: Theme.of(context).textTheme.bodyLarge?.copyWith( color: Colors.white.withValues(alpha: 0.55), ), ), const SizedBox(height: 8), Container( padding: const EdgeInsets.symmetric( horizontal: 14, vertical: 8, ), decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.06), borderRadius: BorderRadius.circular(20), border: Border.all( color: Colors.white.withValues(alpha: 0.08), ), ), child: Text( 'Sesión activa · $expiresHours h', style: TextStyle( color: Colors.white.withValues(alpha: 0.45), fontSize: 13, ), ), ), const SizedBox(height: 48), _ScanButton(onPressed: () => _openScanner(context)), ], ), ), ), ], ), ), ], ), ); } } class _ScanButton extends StatefulWidget { const _ScanButton({required this.onPressed}); final VoidCallback onPressed; @override State<_ScanButton> createState() => _ScanButtonState(); } class _ScanButtonState extends State<_ScanButton> with SingleTickerProviderStateMixin { late final AnimationController _controller; late final Animation _glow; @override void initState() { super.initState(); _controller = AnimationController( vsync: this, duration: const Duration(milliseconds: 1800), )..repeat(reverse: true); _glow = Tween(begin: 0.18, end: 0.38).animate( CurvedAnimation(parent: _controller, curve: Curves.easeInOut), ); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return AnimatedBuilder( animation: _glow, builder: (context, child) { return Container( width: double.infinity, decoration: BoxDecoration( borderRadius: BorderRadius.circular(20), boxShadow: [ BoxShadow( color: AppTheme.primary.withValues(alpha: _glow.value), blurRadius: 28, offset: const Offset(0, 10), ), ], ), child: child, ); }, child: Material( color: Colors.transparent, child: InkWell( onTap: widget.onPressed, borderRadius: BorderRadius.circular(20), child: Ink( decoration: BoxDecoration( borderRadius: BorderRadius.circular(20), gradient: const LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [AppTheme.primary, AppTheme.primaryDark], ), ), child: Padding( padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 24), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( width: 44, height: 44, decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.18), borderRadius: BorderRadius.circular(12), ), child: const Icon( Icons.qr_code_scanner_rounded, color: Colors.white, size: 26, ), ), const SizedBox(width: 16), const Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Abrir escáner', style: TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.w700, ), ), Text( 'Validar código de pase', style: TextStyle( color: Color(0xCCFFFFFF), fontSize: 13, ), ), ], ), const Spacer(), const Icon( Icons.arrow_forward_rounded, color: Colors.white, ), ], ), ), ), ), ), ); } }