// ignore_for_file: deprecated_member_use import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../application/auth_controller.dart'; import '../../../data/services/biometric_service.dart'; import '../../../core/widgets/app_scaffold.dart'; import '../../../core/widgets/primary_button.dart'; import '../../../core/utils/validators.dart'; class SignInScreen extends ConsumerStatefulWidget { const SignInScreen({super.key}); @override ConsumerState createState() => _SignInScreenState(); } class _SignInScreenState extends ConsumerState { final _formKey = GlobalKey(); final _emailController = TextEditingController(); final _passwordController = TextEditingController(); final BiometricService _biometricService = BiometricService(); bool _isLoading = false; bool _obscurePassword = true; bool _isBiometricAvailable = false; bool _isBiometricEnabled = false; bool _isBiometricLoading = false; bool _showEmailVerificationMessage = false; @override void initState() { super.initState(); _checkBiometricStatus(); _checkIfComingFromRegistration(); } void _checkIfComingFromRegistration() async { // Check if user navigated from registration by checking shared preferences final prefs = await SharedPreferences.getInstance(); final justRegistered = prefs.getBool('just_registered') ?? false; final registrationTime = prefs.getInt('registration_time') ?? 0; if (mounted) { // Show message if registration happened in the last 5 minutes final now = DateTime.now().millisecondsSinceEpoch; final fiveMinutesAgo = now - (5 * 60 * 1000); if (justRegistered && registrationTime > fiveMinutesAgo) { setState(() { _showEmailVerificationMessage = true; }); // Clear the flag so it doesn't show again await prefs.remove('just_registered'); await prefs.remove('registration_time'); } } } Future _checkBiometricStatus() async { try { final availability = await _biometricService.checkAvailability(); final isEnabled = await _biometricService.isBiometricEnabled(); if (mounted) { setState(() { _isBiometricAvailable = availability == BiometricAvailability.available; _isBiometricEnabled = isEnabled; }); } } catch (e) { // Biometric not available, ignore } } Future _handleBiometricSignIn() async { setState(() => _isBiometricLoading = true); try { final authController = ref.read(authControllerProvider.notifier); final success = await authController.signInWithBiometric(); if (success) { // Navigation will be handled by AuthGate } else { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Biometric login failed')), ); } } } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Biometric login error: ${e.toString()}')), ); } } finally { if (mounted) { setState(() => _isBiometricLoading = false); } } } Future _handleSignIn() async { if (!_formKey.currentState!.validate()) return; setState(() => _isLoading = true); try { await ref.read(authControllerProvider.notifier).signInWithEmail( _emailController.text.trim(), _passwordController.text, ); } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Sign in failed: $e')), ); } } finally { if (mounted) { setState(() => _isLoading = false); } } } Future _handleResetPassword() async { if (_emailController.text.trim().isEmpty) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Please enter your email address')), ); return; } try { await ref.read(authControllerProvider.notifier).resetPassword( _emailController.text.trim(), ); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Password reset email sent')), ); } } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Failed to send reset email: $e')), ); } } } @override void dispose() { _emailController.dispose(); _passwordController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return AppScaffold( body: Center( child: SingleChildScrollView( padding: const EdgeInsets.all(24.0), child: Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 420), child: Container( padding: const EdgeInsets.symmetric( horizontal: 24, vertical: 32, ), decoration: BoxDecoration( color: Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(32), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.06), blurRadius: 40, offset: const Offset(0, 24), ), ], ), child: Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( '1356', style: Theme.of(context) .textTheme .titleMedium ?.copyWith( fontWeight: FontWeight.w600, ), ), Container( padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 6, ), decoration: BoxDecoration( color: Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(999), border: Border.all( color: Theme.of(context) .colorScheme .onSurface .withOpacity(0.08), ), ), child: Text( 'Sign in', style: Theme.of(context) .textTheme .labelMedium ?.copyWith( color: Theme.of(context) .colorScheme .onSurface .withOpacity(0.8), ), ), ), ], ), const SizedBox(height: 24), Text( 'Welcome back', style: Theme.of(context) .textTheme .headlineMedium ?.copyWith( fontWeight: FontWeight.bold, ), ), const SizedBox(height: 8), Text( 'Sign in to continue your journey', style: Theme.of(context) .textTheme .bodyLarge ?.copyWith( color: Theme.of(context) .colorScheme .onSurface .withOpacity(0.7), ), ), const SizedBox(height: 16), // Email verification reminder message if (_showEmailVerificationMessage) Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.blue.withOpacity(0.1), borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.blue.withOpacity(0.3)), ), child: Row( children: [ Icon(Icons.info_outline, color: Colors.blue.shade700, size: 20), const SizedBox(width: 8), Expanded( child: Text( 'Please verify your email before signing in. Check your inbox for the verification link.', style: TextStyle( color: Colors.blue.shade700, fontSize: 12, ), ), ), IconButton( icon: const Icon(Icons.close, size: 16), onPressed: () { setState(() { _showEmailVerificationMessage = false; }); }, color: Colors.blue.shade700, ), ], ), ), if (_showEmailVerificationMessage) const SizedBox(height: 16), const SizedBox(height: 24), Semantics( label: 'Email address field', hint: 'Enter your email address', child: TextFormField( controller: _emailController, keyboardType: TextInputType.emailAddress, textInputAction: TextInputAction.next, decoration: const InputDecoration( labelText: 'Email', prefixIcon: Icon(Icons.email_outlined), ), validator: Validators.validateEmail, enabled: !_isLoading, ), ), const SizedBox(height: 16), Semantics( label: 'Password field', hint: 'Enter your password', child: TextFormField( controller: _passwordController, obscureText: _obscurePassword, textInputAction: TextInputAction.done, decoration: InputDecoration( labelText: 'Password', prefixIcon: const Icon(Icons.lock_outlined), suffixIcon: Semantics( button: true, label: _obscurePassword ? 'Show password' : 'Hide password', child: IconButton( icon: Icon( _obscurePassword ? Icons.visibility_outlined : Icons.visibility_off_outlined, ), onPressed: () { setState( () => _obscurePassword = !_obscurePassword, ); }, ), ), ), validator: Validators.validatePassword, enabled: !_isLoading, onFieldSubmitted: (_) => _handleSignIn(), ), ), const SizedBox(height: 8), Align( alignment: Alignment.centerRight, child: Semantics( button: true, label: 'Forgot password button', hint: 'Tap to reset your password', child: TextButton( onPressed: _isLoading ? () {} : _handleResetPassword, child: const Text('Forgot password?'), ), ), ), const SizedBox(height: 24), // Biometric Login Button if (_isBiometricAvailable && _isBiometricEnabled) Container( width: double.infinity, height: 48, child: ElevatedButton.icon( onPressed: _isBiometricLoading ? null : _handleBiometricSignIn, icon: _isBiometricLoading ? const SizedBox( width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2), ) : const Icon(Icons.fingerprint), label: Text(_isBiometricLoading ? 'Authenticating...' : 'Sign in with Biometric'), style: ElevatedButton.styleFrom( backgroundColor: Theme.of(context).colorScheme.surface, foregroundColor: Theme.of(context).colorScheme.onSurface, elevation: 0, side: BorderSide( color: Theme.of(context).colorScheme.outline, ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), ), ), if (_isBiometricAvailable && _isBiometricEnabled) const SizedBox(height: 12), PrimaryButton( onPressed: _handleSignIn, text: _isLoading ? 'Signing in...' : 'Sign In', isLoading: _isLoading, ), const SizedBox(height: 16), Semantics( button: true, label: 'Sign up button', hint: 'Tap to create a new account', child: TextButton( onPressed: _isLoading ? () {} : () => context.push('/sign-up'), child: const Text( "Don't have an account? Sign up", ), ), ), ], ), ), ), ), ), ), ), ); } }