diff --git a/lib/core/routing/app_router.dart b/lib/core/routing/app_router.dart index fdc1ab1..89514d4 100644 --- a/lib/core/routing/app_router.dart +++ b/lib/core/routing/app_router.dart @@ -126,10 +126,9 @@ class AppRouter { ), ], - // Redirect unauthenticated users to login + // Let splash screen handle all navigation logic redirect: (context, state) { - // TODO: Implement authentication check - // For now, allow all routes + // Allow all routes - splash screen will handle authentication and navigation return null; }, ); diff --git a/lib/core/services/auth_service.dart b/lib/core/services/auth_service.dart index 2f85070..70c5e96 100644 --- a/lib/core/services/auth_service.dart +++ b/lib/core/services/auth_service.dart @@ -1,4 +1,5 @@ import 'package:firebase_auth/firebase_auth.dart'; +import 'session_service.dart'; /// Service for handling Firebase Authentication class AuthService { @@ -90,12 +91,38 @@ class AuthService { try { print('DEBUG: Tentando fazer logout'); await _auth.signOut(); - print('DEBUG: Logout realizado com sucesso'); + + // Clear saved session + await SessionService.clearSession(); + + print('DEBUG: Logout e limpeza de sessão realizados com sucesso'); } catch (e) { print('DEBUG: Erro ao fazer logout: $e'); } } + /// Attempt auto-login with saved credentials + static Future attemptAutoLogin() async { + try { + final sessionData = await SessionService.shouldAutoLogin(); + + if (sessionData['shouldAutoLogin'] == true) { + final email = sessionData['email']; + print('DEBUG: Attempting auto-login for: $email'); + + // Note: For security reasons, we cannot auto-login without password + // This method just checks if auto-login is available + // The actual login still requires user to enter password + return true; + } + + return false; + } catch (e) { + print('DEBUG: Error in auto-login attempt: $e'); + return false; + } + } + /// Get user-friendly error message static String _getErrorMessage(String code) { print('DEBUG: Processando código de erro: $code'); diff --git a/lib/core/services/session_service.dart b/lib/core/services/session_service.dart new file mode 100644 index 0000000..d7cc134 --- /dev/null +++ b/lib/core/services/session_service.dart @@ -0,0 +1,130 @@ +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:firebase_auth/firebase_auth.dart'; + +/// Service for managing persistent user sessions +class SessionService { + static const String _rememberMeKey = 'remember_me'; + static const String _userEmailKey = 'user_email'; + static const String _userDisplayNameKey = 'user_display_name'; + static const String _lastLoginTimeKey = 'last_login_time'; + + /// Save session preferences when user chooses "Remember me" + static Future saveSession({ + required bool rememberMe, + required String email, + String? displayName, + }) async { + try { + final prefs = await SharedPreferences.getInstance(); + + // Save remember me preference + await prefs.setBool(_rememberMeKey, rememberMe); + + if (rememberMe) { + // Save user details for auto-login + await prefs.setString(_userEmailKey, email); + if (displayName != null) { + await prefs.setString(_userDisplayNameKey, displayName); + } + await prefs.setInt(_lastLoginTimeKey, DateTime.now().millisecondsSinceEpoch); + } else { + // Clear saved session if remember me is unchecked + await clearSession(); + } + + print('DEBUG: Session saved - Remember me: $rememberMe, Email: $email'); + } catch (e) { + print('DEBUG: Error saving session: $e'); + } + } + + /// Check if user should be automatically logged in + static Future> shouldAutoLogin() async { + try { + final prefs = await SharedPreferences.getInstance(); + + final rememberMe = prefs.getBool(_rememberMeKey) ?? false; + final email = prefs.getString(_userEmailKey); + final displayName = prefs.getString(_userDisplayNameKey); + final lastLoginTime = prefs.getInt(_lastLoginTimeKey); + + print('DEBUG: Auto-login check - Remember me: $rememberMe, Email: $email'); + + if (rememberMe && email != null) { + // Check if session is not too old (optional: 30 days) + if (lastLoginTime != null) { + final lastLogin = DateTime.fromMillisecondsSinceEpoch(lastLoginTime); + final now = DateTime.now(); + final daysSinceLogin = now.difference(lastLogin).inDays; + + if (daysSinceLogin <= 30) { + return { + 'shouldAutoLogin': true, + 'email': email, + 'displayName': displayName, + }; + } else { + print('DEBUG: Session too old, clearing session'); + await clearSession(); + } + } + } + + return {'shouldAutoLogin': false}; + } catch (e) { + print('DEBUG: Error checking auto-login: $e'); + return {'shouldAutoLogin': false}; + } + } + + /// Clear saved session data + static Future clearSession() async { + try { + final prefs = await SharedPreferences.getInstance(); + await prefs.remove(_rememberMeKey); + await prefs.remove(_userEmailKey); + await prefs.remove(_userDisplayNameKey); + await prefs.remove(_lastLoginTimeKey); + + print('DEBUG: Session cleared'); + } catch (e) { + print('DEBUG: Error clearing session: $e'); + } + } + + /// Get current session info + static Future> getCurrentSession() async { + try { + final prefs = await SharedPreferences.getInstance(); + + return { + 'rememberMe': prefs.getBool(_rememberMeKey) ?? false, + 'email': prefs.getString(_userEmailKey), + 'displayName': prefs.getString(_userDisplayNameKey), + 'lastLoginTime': prefs.getInt(_lastLoginTimeKey), + }; + } catch (e) { + print('DEBUG: Error getting current session: $e'); + return {}; + } + } + + /// Update session with current Firebase user + static Future updateSessionWithCurrentUser() async { + try { + final user = FirebaseAuth.instance.currentUser; + if (user != null) { + final currentSession = await getCurrentSession(); + if (currentSession['rememberMe'] == true) { + await saveSession( + rememberMe: true, + email: user.email ?? '', + displayName: user.displayName, + ); + } + } + } catch (e) { + print('DEBUG: Error updating session with current user: $e'); + } + } +} diff --git a/lib/features/auth/presentation/pages/login_page.dart b/lib/features/auth/presentation/pages/login_page.dart index 042a982..fc4cd6d 100644 --- a/lib/features/auth/presentation/pages/login_page.dart +++ b/lib/features/auth/presentation/pages/login_page.dart @@ -3,6 +3,7 @@ import 'package:flutter_animate/flutter_animate.dart'; import 'package:go_router/go_router.dart'; import '../../../../l10n/app_localizations.dart'; import '../../../../core/services/auth_service.dart'; +import '../../../../core/services/session_service.dart'; import '../../../../shared/presentation/widgets/custom_notification.dart'; class LoginPage extends StatefulWidget { @@ -18,6 +19,7 @@ class _LoginPageState extends State { final _passwordController = TextEditingController(); bool _isLoading = false; bool _obscurePassword = true; + bool _rememberMe = false; @override void dispose() { @@ -26,6 +28,26 @@ class _LoginPageState extends State { super.dispose(); } + @override + void initState() { + super.initState(); + _loadSavedSession(); + } + + void _loadSavedSession() async { + try { + final sessionData = await SessionService.getCurrentSession(); + if (sessionData['email'] != null) { + setState(() { + _emailController.text = sessionData['email']; + _rememberMe = sessionData['rememberMe'] ?? false; + }); + } + } catch (e) { + print('DEBUG: Error loading saved session: $e'); + } + } + void _handleLogin() async { if (_formKey.currentState!.validate()) { setState(() { @@ -45,7 +67,16 @@ class _LoginPageState extends State { password: password, ); - print('DEBUG: Login Firebase bem-sucedido, navegando para dashboard'); + print('DEBUG: Login Firebase bem-sucedido, salvando sessão'); + + // Save session based on remember me preference + await SessionService.saveSession( + rememberMe: _rememberMe, + email: email, + displayName: AuthService.currentUser?.displayName, + ); + + print('DEBUG: Sessão salva, navegando para dashboard'); // Navigate to student dashboard after successful login if (mounted) { @@ -282,7 +313,41 @@ class _LoginPageState extends State { return null; }, ), - const SizedBox(height: 24), + const SizedBox(height: 12), + + // Remember me checkbox + Row( + children: [ + Checkbox( + value: _rememberMe, + onChanged: (bool? value) { + setState(() { + _rememberMe = value ?? false; + }); + }, + activeColor: const Color(0xFF82C9BD), + checkColor: Colors.white, + ), + GestureDetector( + onTap: () { + setState(() { + _rememberMe = !_rememberMe; + }); + }, + child: Text( + 'Manter sessão iniciada', + style: TextStyle( + color: const Color(0xFF2D3748), + fontSize: 14, + fontWeight: _rememberMe + ? FontWeight.w500 + : FontWeight.normal, + ), + ), + ), + ], + ), + const SizedBox(height: 12), // Login button SizedBox( diff --git a/lib/features/splash/presentation/pages/splash_page.dart b/lib/features/splash/presentation/pages/splash_page.dart index 4409212..6c7767a 100644 --- a/lib/features/splash/presentation/pages/splash_page.dart +++ b/lib/features/splash/presentation/pages/splash_page.dart @@ -2,6 +2,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_animate/flutter_animate.dart'; import 'package:go_router/go_router.dart'; import '../../../../core/theme/app_colors.dart'; +import '../../../../core/services/auth_service.dart'; +import '../../../../core/services/session_service.dart'; import '../../../../l10n/app_localizations.dart'; class SplashPage extends StatefulWidget { @@ -15,13 +17,58 @@ class _SplashPageState extends State { @override void initState() { super.initState(); - _navigateToRoleSelection(); + _checkAuthenticationAndNavigate(); } - void _navigateToRoleSelection() async { - await Future.delayed(const Duration(seconds: 3)); - if (mounted) { - context.go('/role-selection'); + void _checkAuthenticationAndNavigate() async { + try { + print('DEBUG: Checking authentication state...'); + + // Always show splash for full 3 seconds for complete animation + await Future.delayed(const Duration(seconds: 3)); + + // Check if user is currently authenticated + final currentUser = AuthService.currentUser; + + if (currentUser != null) { + print('DEBUG: User already authenticated: ${currentUser.email}'); + + // Update session with current user if needed + await SessionService.updateSessionWithCurrentUser(); + + // Navigate to dashboard + if (mounted) { + print('DEBUG: Navigating to student dashboard'); + context.go('/student-dashboard'); + } + return; + } + + // Check if user should be auto-logged in + final sessionData = await SessionService.shouldAutoLogin(); + + if (sessionData['shouldAutoLogin'] == true) { + print('DEBUG: Auto-login available for: ${sessionData['email']}'); + + if (mounted) { + // Navigate to login page with pre-filled data + print('DEBUG: Navigating to login for auto-login'); + context.go('/login'); + } + } else { + print('DEBUG: No auto-login available, going to role selection'); + + if (mounted) { + context.go('/role-selection'); + } + } + } catch (e) { + print('DEBUG: Error in authentication check: $e'); + + // Fallback to role selection + if (mounted) { + context.go('/role-selection'); + } } }