import 'package:flutter/material.dart'; import 'package:qrscanner/config/api_config.dart'; import 'package:qrscanner/models/auth_session.dart'; import 'package:qrscanner/services/auth_service.dart'; import 'package:qrscanner/services/scan_launch_service.dart'; import 'package:qrscanner/services/scan_navigation_service.dart'; import 'package:qrscanner/theme/app_theme.dart'; class LoginScreen extends StatefulWidget { const LoginScreen({super.key}); @override State createState() => _LoginScreenState(); } class _LoginScreenState extends State with SingleTickerProviderStateMixin { final _formKey = GlobalKey(); final _usernameController = TextEditingController(); final _passwordController = TextEditingController(); final _authService = AuthService(); late final AnimationController _animationController; late final Animation _fadeAnimation; late final Animation _slideAnimation; bool _obscurePassword = true; bool _isLoading = false; bool _rememberMe = false; String? _errorMessage; @override void initState() { super.initState(); _animationController = AnimationController( vsync: this, duration: const Duration(milliseconds: 900), ); _fadeAnimation = CurvedAnimation( parent: _animationController, curve: Curves.easeOut, ); _slideAnimation = Tween( begin: const Offset(0, 0.08), end: Offset.zero, ).animate(CurvedAnimation( parent: _animationController, curve: Curves.easeOutCubic, )); _animationController.forward(); ScanLaunchService.instance.claimScanner(); _checkStoredSession(); } Future _checkStoredSession() async { final session = await _authService.loadStoredSession(); if (!mounted || session == null) return; await ScanNavigationService.instance.navigateToHome(session); } @override void dispose() { _animationController.dispose(); _usernameController.dispose(); _passwordController.dispose(); super.dispose(); } Future _handleLogin() async { if (!_formKey.currentState!.validate()) return; setState(() { _isLoading = true; _errorMessage = null; }); try { final response = await _authService.login( username: _usernameController.text.trim(), password: _passwordController.text, rememberMe: _rememberMe, ); if (!mounted) return; if (response.success && response.accessToken != null) { await ScanNavigationService.instance.navigateToHome( AuthSession.fromLoginResponse( response.copyWith( username: response.username ?? _usernameController.text.trim(), ), ), ); return; } setState(() { _errorMessage = response.message ?? 'Credenciales incorrectas'; }); } catch (_) { if (!mounted) return; setState(() { _errorMessage = 'No se pudo conectar al servidor. Revisa API_BASE_URL (${ApiConfig.baseUrl})'; }); } finally { if (mounted) { setState(() => _isLoading = false); } } } @override Widget build(BuildContext context) { return Scaffold( body: Stack( children: [ _buildBackground(), SafeArea( child: FadeTransition( opacity: _fadeAnimation, child: SlideTransition( position: _slideAnimation, child: Center( child: SingleChildScrollView( padding: const EdgeInsets.symmetric(horizontal: 28), child: Column( children: [ _buildHeader(), const SizedBox(height: 40), _buildLoginCard(), const SizedBox(height: 24), _buildFooter(), ], ), ), ), ), ), ), ], ), ); } Widget _buildBackground() { return Container( decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ AppTheme.backgroundDark, Color(0xFF0D1B2A), Color(0xFF1A1A3E), ], ), ), child: Stack( children: [ Positioned( top: -80, right: -60, child: Container( width: 260, height: 260, decoration: BoxDecoration( shape: BoxShape.circle, gradient: RadialGradient( colors: [ AppTheme.primary.withValues(alpha: 0.15), Colors.transparent, ], ), ), ), ), Positioned( bottom: -100, left: -80, child: Container( width: 300, height: 300, decoration: BoxDecoration( shape: BoxShape.circle, gradient: RadialGradient( colors: [ AppTheme.accent.withValues(alpha: 0.12), Colors.transparent, ], ), ), ), ), ], ), ); } Widget _buildHeader() { return Column( children: [ Container( width: 88, height: 88, decoration: BoxDecoration( borderRadius: BorderRadius.circular(22), gradient: const LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [AppTheme.primary, AppTheme.primaryDark], ), boxShadow: [ BoxShadow( color: AppTheme.primary.withValues(alpha: 0.35), blurRadius: 24, offset: const Offset(0, 8), ), ], ), child: const Icon( Icons.qr_code_scanner_rounded, size: 44, color: Colors.white, ), ), const SizedBox(height: 20), Text( 'QR Scanner', style: Theme.of(context).textTheme.headlineMedium?.copyWith( fontWeight: FontWeight.bold, letterSpacing: -0.5, ), ), const SizedBox(height: 8), Text( 'Inicia sesión para continuar', style: Theme.of(context).textTheme.bodyMedium?.copyWith( color: Colors.white.withValues(alpha: 0.55), ), ), ], ); } Widget _buildLoginCard() { return Container( width: double.infinity, padding: const EdgeInsets.all(28), decoration: BoxDecoration( color: AppTheme.surfaceDark.withValues(alpha: 0.7), borderRadius: BorderRadius.circular(24), border: Border.all(color: Colors.white.withValues(alpha: 0.08)), boxShadow: [ BoxShadow( color: Colors.black.withValues(alpha: 0.3), blurRadius: 32, offset: const Offset(0, 16), ), ], ), child: Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ if (_errorMessage != null) ...[ Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.red.withValues(alpha: 0.12), borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.red.withValues(alpha: 0.3)), ), child: Text( _errorMessage!, style: const TextStyle(color: Color(0xFFFF8A80), fontSize: 13), ), ), const SizedBox(height: 16), ], TextFormField( controller: _usernameController, keyboardType: TextInputType.text, textInputAction: TextInputAction.next, autocorrect: false, style: const TextStyle(color: Colors.white), decoration: InputDecoration( hintText: 'Usuario', prefixIcon: Icon( Icons.person_outline_rounded, color: Colors.white.withValues(alpha: 0.5), ), ), validator: (value) { if (value == null || value.trim().isEmpty) { return 'Ingresa tu usuario'; } return null; }, ), const SizedBox(height: 16), TextFormField( controller: _passwordController, obscureText: _obscurePassword, textInputAction: TextInputAction.done, onFieldSubmitted: (_) => _handleLogin(), style: const TextStyle(color: Colors.white), decoration: InputDecoration( hintText: 'Contraseña', prefixIcon: Icon( Icons.lock_outline_rounded, color: Colors.white.withValues(alpha: 0.5), ), suffixIcon: IconButton( icon: Icon( _obscurePassword ? Icons.visibility_off_outlined : Icons.visibility_outlined, color: Colors.white.withValues(alpha: 0.5), ), onPressed: () { setState(() => _obscurePassword = !_obscurePassword); }, ), ), validator: (value) { if (value == null || value.isEmpty) { return 'Ingresa tu contraseña'; } return null; }, ), const SizedBox(height: 12), Row( children: [ SizedBox( height: 36, child: Checkbox( value: _rememberMe, activeColor: AppTheme.primary, side: BorderSide( color: Colors.white.withValues(alpha: 0.3), ), onChanged: (value) { setState(() => _rememberMe = value ?? false); }, ), ), Text( 'Recordarme', style: TextStyle( color: Colors.white.withValues(alpha: 0.7), fontSize: 14, ), ), ], ), const SizedBox(height: 8), _buildLoginButton(), const SizedBox(height: 16), Text( 'API: ${ApiConfig.baseUrl}', textAlign: TextAlign.center, style: TextStyle( color: Colors.white.withValues(alpha: 0.35), fontSize: 11, ), ), ], ), ), ); } Widget _buildLoginButton() { return DecoratedBox( decoration: BoxDecoration( borderRadius: BorderRadius.circular(14), gradient: const LinearGradient( colors: [AppTheme.primary, AppTheme.primaryDark], ), boxShadow: [ BoxShadow( color: AppTheme.primary.withValues(alpha: 0.3), blurRadius: 16, offset: const Offset(0, 6), ), ], ), child: ElevatedButton( onPressed: _isLoading ? null : _handleLogin, style: ElevatedButton.styleFrom( backgroundColor: Colors.transparent, shadowColor: Colors.transparent, disabledBackgroundColor: Colors.transparent, ), child: _isLoading ? const SizedBox( height: 22, width: 22, child: CircularProgressIndicator( strokeWidth: 2.5, color: Colors.white, ), ) : const Text( 'Iniciar sesión', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: Colors.white, ), ), ), ); } Widget _buildFooter() { return Text( 'POST /api/auth/login', style: TextStyle( color: Colors.white.withValues(alpha: 0.35), fontSize: 12, ), ); } }