import 'package:flutter/material.dart'; import 'package:inou_app/design/inou_theme.dart'; import 'package:inou_app/design/inou_text.dart'; import 'package:inou_app/design/widgets/widgets.dart'; /// Login page with biometric support class LoginPage extends StatefulWidget { const LoginPage({super.key}); @override State createState() => _LoginPageState(); } class _LoginPageState extends State { final _formKey = GlobalKey(); final _emailController = TextEditingController(); final _passwordController = TextEditingController(); bool _isLoading = false; bool _obscurePassword = true; bool _rememberMe = false; // Biometric state bool _biometricAvailable = false; bool _biometricEnrolled = false; @override void initState() { super.initState(); _checkBiometricAvailability(); } Future _checkBiometricAvailability() async { // TODO: Check actual biometric availability using local_auth // For now, simulate availability on mobile setState(() { _biometricAvailable = true; // Placeholder _biometricEnrolled = false; // Placeholder - check if user has enrolled }); } @override void dispose() { _emailController.dispose(); _passwordController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return InouAuthFlowPage( child: Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text( 'Welcome back', style: InouText.sectionTitle.copyWith(fontWeight: FontWeight.w600), textAlign: TextAlign.center, ), const SizedBox(height: 8), Text( 'Sign in to access your health dossier', style: InouText.body.copyWith( color: InouTheme.textMuted, ), textAlign: TextAlign.center, ), const SizedBox(height: 32), // Biometric login button (if available and enrolled) if (_biometricAvailable && _biometricEnrolled) ...[ _buildBiometricButton(), const SizedBox(height: 24), _buildDivider('or sign in with email'), const SizedBox(height: 24), ], // Email field InouTextField( label: 'Email', controller: _emailController, placeholder: 'you@example.com', keyboardType: TextInputType.emailAddress, autofillHints: const [AutofillHints.email], validator: (value) { if (value == null || value.isEmpty) { return 'Please enter your email'; } if (!value.contains('@')) { return 'Please enter a valid email'; } return null; }, ), const SizedBox(height: 16), // Password field InouTextField( label: 'Password', controller: _passwordController, placeholder: '••••••••', obscureText: _obscurePassword, autofillHints: const [AutofillHints.password], suffixIcon: IconButton( icon: Icon( _obscurePassword ? Icons.visibility_off : Icons.visibility, color: InouTheme.textMuted, size: 20, ), onPressed: () { setState(() => _obscurePassword = !_obscurePassword); }, ), validator: (value) { if (value == null || value.isEmpty) { return 'Please enter your password'; } return null; }, ), const SizedBox(height: 16), // Remember me & forgot password row Row( children: [ InouCheckbox( value: _rememberMe, label: 'Remember me', onChanged: (value) { setState(() => _rememberMe = value ?? false); }, ), const Spacer(), TextButton( onPressed: _handleForgotPassword, child: Text( 'Forgot password?', style: InouText.bodySmall.copyWith( color: InouTheme.accent, ), ), ), ], ), const SizedBox(height: 24), // Login button InouButton( text: _isLoading ? 'Signing in...' : 'Sign in', onPressed: _isLoading ? null : _handleLogin, ), const SizedBox(height: 24), // Sign up link Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( 'Don\'t have an account? ', style: InouText.body.copyWith( color: InouTheme.textMuted, ), ), TextButton( onPressed: () => Navigator.pushReplacementNamed(context, '/signup'), style: TextButton.styleFrom( padding: EdgeInsets.zero, minimumSize: Size.zero, tapTargetSize: MaterialTapTargetSize.shrinkWrap, ), child: Text( 'Sign up', style: InouText.body.copyWith( color: InouTheme.accent, fontWeight: FontWeight.w600, ), ), ), ], ), ], ), ), ); } Widget _buildBiometricButton() { return OutlinedButton.icon( onPressed: _handleBiometricLogin, icon: const Icon(Icons.fingerprint, size: 24), label: const Text('Sign in with biometrics'), style: OutlinedButton.styleFrom( foregroundColor: InouTheme.text, side: BorderSide(color: InouTheme.border), padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder( borderRadius: InouTheme.borderRadiusMd, ), ), ); } Widget _buildDivider(String text) { return Row( children: [ Expanded(child: Divider(color: InouTheme.border)), Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Text( text, style: InouText.bodySmall.copyWith( color: InouTheme.textMuted, ), ), ), Expanded(child: Divider(color: InouTheme.border)), ], ); } Future _handleLogin() async { if (!_formKey.currentState!.validate()) return; setState(() => _isLoading = true); try { // TODO: Implement actual login await Future.delayed(const Duration(seconds: 1)); if (mounted) { // After successful login, offer biometric enrollment if available if (_biometricAvailable && !_biometricEnrolled) { await _offerBiometricEnrollment(); } // Navigate to dashboard Navigator.pushNamedAndRemoveUntil( context, '/dashboard', (route) => false, ); } } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Login failed: ${e.toString()}'), backgroundColor: InouTheme.danger, behavior: SnackBarBehavior.floating, ), ); } } finally { if (mounted) { setState(() => _isLoading = false); } } } Future _handleBiometricLogin() async { // TODO: Implement biometric authentication using local_auth // On success, retrieve stored credentials and login } Future _handleForgotPassword() async { // Navigate to forgot password flow Navigator.pushNamed(context, '/forgot-password'); } Future _offerBiometricEnrollment() async { final shouldEnroll = await showDialog( context: context, builder: (context) => AlertDialog( shape: RoundedRectangleBorder( borderRadius: InouTheme.borderRadiusLg, ), title: const Text('Enable biometric login?'), content: const Text( 'Sign in faster next time using your fingerprint or face.', ), actions: [ TextButton( onPressed: () => Navigator.pop(context, false), child: Text( 'Not now', style: TextStyle(color: InouTheme.textMuted), ), ), ElevatedButton( onPressed: () => Navigator.pop(context, true), style: ElevatedButton.styleFrom( backgroundColor: InouTheme.accent, ), child: const Text('Enable'), ), ], ), ); if (shouldEnroll == true) { // TODO: Enroll biometric credentials // Store encrypted credentials securely } } }