Début refacto en MVVM (Login, drawer OK

This commit is contained in:
2025-03-10 23:40:54 +01:00
parent b6f169e5f7
commit 2b8e7085aa
15 changed files with 509 additions and 330 deletions

View File

@ -1,5 +1,4 @@
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:em2rp/providers/user_provider.dart';
import 'package:em2rp/view_model/login_view_model.dart';
import 'package:em2rp/views/widgets/auth/mail_textfield.dart';
import 'package:em2rp/views/widgets/error_message.dart';
import 'package:em2rp/views/widgets/auth/forgot_password_button.dart';
@ -8,117 +7,25 @@ import 'package:em2rp/views/widgets/auth/login_button.dart';
import 'package:em2rp/views/widgets/image/em2_logo_n_sur_b.dart';
import 'package:em2rp/views/widgets/auth/password_textfield.dart';
import 'package:em2rp/views/widgets/auth/welcome_text.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:em2rp/views/widgets/auth/forgot_password_dialog.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class LoginPage extends StatefulWidget {
class LoginPage extends StatelessWidget {
const LoginPage({super.key});
@override
State<LoginPage> createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
String _errorMessage = '';
bool _isLoading = false;
bool _obscurePassword = true;
bool _highlightPasswordField = false;
bool _highlightEmailField = false;
@override
void dispose() {
_emailController.dispose();
_passwordController.dispose();
super.dispose();
}
Future<void> _signInWithEmailAndPassword() async {
// Pas de vérification mounted ici, le widget reste monté pendant toute l'opération
setState(() {
_errorMessage = '';
_isLoading = true;
_highlightPasswordField = false;
_highlightEmailField = false;
});
try {
final UserCredential userCredential =
await FirebaseAuth.instance.signInWithEmailAndPassword(
email: _emailController.text.trim(),
password: _passwordController.text,
);
final uid = userCredential.user!.uid;
final DocumentSnapshot userDoc =
await FirebaseFirestore.instance.collection('users').doc(uid).get();
if (userDoc.exists) {
print("User document found: ${userDoc.data()}");
Provider.of<UserProvider>(context, listen: false)
.setUserData(userDoc.data() as Map<String, dynamic>, uid);
// Maintenant que toutes les données sont chargées, naviguer vers CalendarPage.
Navigator.of(context).pushReplacementNamed('/calendar');
} else {
if (!mounted) return;
setState(() {
_errorMessage = "Aucune donnée utilisateur trouvée.";
_isLoading = false;
});
}
} on FirebaseAuthException catch (e) {
if (!mounted) return;
setState(() {
_isLoading = false;
if (e.code == 'user-not-found' || e.code == 'wrong-password') {
_errorMessage = "Email ou mot de passe incorrect.";
_highlightPasswordField = true;
_highlightEmailField = true;
} else {
_errorMessage = "Erreur de connexion. Veuillez réessayer.";
}
});
} catch (e) {
if (!mounted) return;
setState(() {
_errorMessage =
"Erreur lors de la récupération des données utilisateur.";
_isLoading = false;
});
}
}
void _togglePasswordVisibility() {
if (!mounted) return;
setState(() {
_obscurePassword = !_obscurePassword;
});
}
void _forgotPassword() {
showDialog(
context: context,
builder: (BuildContext context) {
return const ForgotPasswordDialogWidget();
},
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth > 900) {
return _buildLargeScreenLayout(context);
} else {
return _buildSmallScreenLayout(context);
}
},
return ChangeNotifierProvider(
create: (_) => LoginViewModel(),
child: Scaffold(
body: LayoutBuilder(
builder: (context, constraints) {
return constraints.maxWidth > 900
? _buildLargeScreenLayout(context)
: _buildSmallScreenLayout(context);
},
),
),
);
}
@ -126,10 +33,7 @@ class _LoginPageState extends State<LoginPage> {
Widget _buildLargeScreenLayout(BuildContext context) {
return Row(
children: <Widget>[
Expanded(
flex: 6,
child: const BigLeftImageWidget(),
),
const Expanded(flex: 6, child: BigLeftImageWidget()),
Expanded(
flex: 4,
child: Padding(
@ -152,34 +56,45 @@ class _LoginPageState extends State<LoginPage> {
}
Widget _buildLoginForm(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
const LogoWidget(),
const SizedBox(height: 30),
const WelcomeTextWidget(),
const SizedBox(height: 40),
EmailTextFieldWidget(
emailController: _emailController,
highlightEmailField: _highlightEmailField,
),
const SizedBox(height: 20),
PasswordTextFieldWidget(
passwordController: _passwordController,
obscurePassword: _obscurePassword,
highlightPasswordField: _highlightPasswordField,
onTogglePasswordVisibility: _togglePasswordVisibility,
),
ForgotPasswordButtonWidget(onPressed: _forgotPassword),
const SizedBox(height: 30),
LoginButtonWidget(
isLoading: _isLoading,
onPressed: _signInWithEmailAndPassword,
),
const SizedBox(height: 20),
ErrorMessageWidget(errorMessage: _errorMessage),
],
return Consumer<LoginViewModel>(
builder: (context, loginViewModel, child) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
const LogoWidget(),
const SizedBox(height: 30),
const WelcomeTextWidget(),
const SizedBox(height: 40),
EmailTextFieldWidget(
emailController: loginViewModel.emailController,
highlightEmailField: loginViewModel.highlightEmailField,
),
const SizedBox(height: 20),
PasswordTextFieldWidget(
passwordController: loginViewModel.passwordController,
obscurePassword: loginViewModel.obscurePassword,
highlightPasswordField: loginViewModel.highlightPasswordField,
onTogglePasswordVisibility:
loginViewModel.togglePasswordVisibility,
),
ForgotPasswordButtonWidget(
onPressed: () => showDialog(
context: context,
builder: (BuildContext context) =>
const ForgotPasswordDialogWidget(),
),
),
const SizedBox(height: 30),
LoginButtonWidget(
isLoading: loginViewModel.isLoading,
onPressed: () => loginViewModel.signIn(context),
),
const SizedBox(height: 20),
ErrorMessageWidget(errorMessage: loginViewModel.errorMessage),
],
);
},
);
}
}