Manter sessão e loading
This commit is contained in:
@@ -126,10 +126,9 @@ class AppRouter {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
||||||
// Redirect unauthenticated users to login
|
// Let splash screen handle all navigation logic
|
||||||
redirect: (context, state) {
|
redirect: (context, state) {
|
||||||
// TODO: Implement authentication check
|
// Allow all routes - splash screen will handle authentication and navigation
|
||||||
// For now, allow all routes
|
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:firebase_auth/firebase_auth.dart';
|
import 'package:firebase_auth/firebase_auth.dart';
|
||||||
|
import 'session_service.dart';
|
||||||
|
|
||||||
/// Service for handling Firebase Authentication
|
/// Service for handling Firebase Authentication
|
||||||
class AuthService {
|
class AuthService {
|
||||||
@@ -90,12 +91,38 @@ class AuthService {
|
|||||||
try {
|
try {
|
||||||
print('DEBUG: Tentando fazer logout');
|
print('DEBUG: Tentando fazer logout');
|
||||||
await _auth.signOut();
|
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) {
|
} catch (e) {
|
||||||
print('DEBUG: Erro ao fazer logout: $e');
|
print('DEBUG: Erro ao fazer logout: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attempt auto-login with saved credentials
|
||||||
|
static Future<bool> 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
|
/// Get user-friendly error message
|
||||||
static String _getErrorMessage(String code) {
|
static String _getErrorMessage(String code) {
|
||||||
print('DEBUG: Processando código de erro: $code');
|
print('DEBUG: Processando código de erro: $code');
|
||||||
|
|||||||
130
lib/core/services/session_service.dart
Normal file
130
lib/core/services/session_service.dart
Normal file
@@ -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<void> 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<Map<String, dynamic>> 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<void> 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<Map<String, dynamic>> 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<void> 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');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ import 'package:flutter_animate/flutter_animate.dart';
|
|||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import '../../../../l10n/app_localizations.dart';
|
import '../../../../l10n/app_localizations.dart';
|
||||||
import '../../../../core/services/auth_service.dart';
|
import '../../../../core/services/auth_service.dart';
|
||||||
|
import '../../../../core/services/session_service.dart';
|
||||||
import '../../../../shared/presentation/widgets/custom_notification.dart';
|
import '../../../../shared/presentation/widgets/custom_notification.dart';
|
||||||
|
|
||||||
class LoginPage extends StatefulWidget {
|
class LoginPage extends StatefulWidget {
|
||||||
@@ -18,6 +19,7 @@ class _LoginPageState extends State<LoginPage> {
|
|||||||
final _passwordController = TextEditingController();
|
final _passwordController = TextEditingController();
|
||||||
bool _isLoading = false;
|
bool _isLoading = false;
|
||||||
bool _obscurePassword = true;
|
bool _obscurePassword = true;
|
||||||
|
bool _rememberMe = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
@@ -26,6 +28,26 @@ class _LoginPageState extends State<LoginPage> {
|
|||||||
super.dispose();
|
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 {
|
void _handleLogin() async {
|
||||||
if (_formKey.currentState!.validate()) {
|
if (_formKey.currentState!.validate()) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@@ -45,7 +67,16 @@ class _LoginPageState extends State<LoginPage> {
|
|||||||
password: password,
|
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
|
// Navigate to student dashboard after successful login
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
@@ -282,7 +313,41 @@ class _LoginPageState extends State<LoginPage> {
|
|||||||
return null;
|
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
|
// Login button
|
||||||
SizedBox(
|
SizedBox(
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_animate/flutter_animate.dart';
|
import 'package:flutter_animate/flutter_animate.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import '../../../../core/theme/app_colors.dart';
|
import '../../../../core/theme/app_colors.dart';
|
||||||
|
import '../../../../core/services/auth_service.dart';
|
||||||
|
import '../../../../core/services/session_service.dart';
|
||||||
import '../../../../l10n/app_localizations.dart';
|
import '../../../../l10n/app_localizations.dart';
|
||||||
|
|
||||||
class SplashPage extends StatefulWidget {
|
class SplashPage extends StatefulWidget {
|
||||||
@@ -15,13 +17,58 @@ class _SplashPageState extends State<SplashPage> {
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_navigateToRoleSelection();
|
_checkAuthenticationAndNavigate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _navigateToRoleSelection() async {
|
void _checkAuthenticationAndNavigate() async {
|
||||||
await Future.delayed(const Duration(seconds: 3));
|
try {
|
||||||
if (mounted) {
|
print('DEBUG: Checking authentication state...');
|
||||||
context.go('/role-selection');
|
|
||||||
|
// 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');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user