diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts
index 746a639..7d93efd 100644
--- a/android/app/build.gradle.kts
+++ b/android/app/build.gradle.kts
@@ -1,5 +1,8 @@
plugins {
id("com.android.application")
+ // START: FlutterFire Configuration
+ id("com.google.gms.google-services")
+ // END: FlutterFire Configuration
id("kotlin-android")
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id("dev.flutter.flutter-gradle-plugin")
diff --git a/android/app/google-services.json b/android/app/google-services.json
new file mode 100644
index 0000000..070c1b2
--- /dev/null
+++ b/android/app/google-services.json
@@ -0,0 +1,54 @@
+{
+ "project_info": {
+ "project_number": "521527250907",
+ "project_id": "travelmate-a47f5",
+ "storage_bucket": "travelmate-a47f5.firebasestorage.app"
+ },
+ "client": [
+ {
+ "client_info": {
+ "mobilesdk_app_id": "1:521527250907:android:be3db7fc84f053ec7da1fe",
+ "android_client_info": {
+ "package_name": "com.example.travel_mate"
+ }
+ },
+ "oauth_client": [
+ {
+ "client_id": "521527250907-lqgj1lmfcsjusm2be9r6kpuanq3jvjcd.apps.googleusercontent.com",
+ "client_type": 1,
+ "android_info": {
+ "package_name": "com.example.travel_mate",
+ "certificate_hash": "9b7e3f14f0fcae0034ed977b5d40305b1812308d"
+ }
+ },
+ {
+ "client_id": "521527250907-j0kt1hc8hc7qc2kedp4akehau754cn5d.apps.googleusercontent.com",
+ "client_type": 3
+ }
+ ],
+ "api_key": [
+ {
+ "current_key": "AIzaSyAON_ol0Jr34tKbETvdDK9JCQdKNawxBeQ"
+ }
+ ],
+ "services": {
+ "appinvite_service": {
+ "other_platform_oauth_client": [
+ {
+ "client_id": "521527250907-j0kt1hc8hc7qc2kedp4akehau754cn5d.apps.googleusercontent.com",
+ "client_type": 3
+ },
+ {
+ "client_id": "521527250907-3i1qe2656eojs8k9hjdi573j09i9p41m.apps.googleusercontent.com",
+ "client_type": 2,
+ "ios_info": {
+ "bundle_id": "com.example.travelMate"
+ }
+ }
+ ]
+ }
+ }
+ }
+ ],
+ "configuration_version": "1"
+}
\ No newline at end of file
diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts
index fb605bc..ff284ff 100644
--- a/android/settings.gradle.kts
+++ b/android/settings.gradle.kts
@@ -20,6 +20,9 @@ pluginManagement {
plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
id("com.android.application") version "8.9.1" apply false
+ // START: FlutterFire Configuration
+ id("com.google.gms.google-services") version("4.3.15") apply false
+ // END: FlutterFire Configuration
id("org.jetbrains.kotlin.android") version "2.1.0" apply false
}
diff --git a/firebase.json b/firebase.json
new file mode 100644
index 0000000..4c11011
--- /dev/null
+++ b/firebase.json
@@ -0,0 +1 @@
+{"flutter":{"platforms":{"android":{"default":{"projectId":"travelmate-a47f5","appId":"1:521527250907:android:be3db7fc84f053ec7da1fe","fileOutput":"android/app/google-services.json"}},"dart":{"lib/firebase_options.dart":{"projectId":"travelmate-a47f5","configurations":{"android":"1:521527250907:android:be3db7fc84f053ec7da1fe","ios":"1:521527250907:ios:64b41be39c54db1c7da1fe","windows":"1:521527250907:web:53ff98bcdb8c218f7da1fe"}}}}}}
\ No newline at end of file
diff --git a/ios/Runner/GoogleService-Info.plist b/ios/Runner/GoogleService-Info.plist
new file mode 100644
index 0000000..a1f738f
--- /dev/null
+++ b/ios/Runner/GoogleService-Info.plist
@@ -0,0 +1,36 @@
+
+
+
+
+ CLIENT_ID
+ 521527250907-3i1qe2656eojs8k9hjdi573j09i9p41m.apps.googleusercontent.com
+ REVERSED_CLIENT_ID
+ com.googleusercontent.apps.521527250907-3i1qe2656eojs8k9hjdi573j09i9p41m
+ ANDROID_CLIENT_ID
+ 521527250907-lqgj1lmfcsjusm2be9r6kpuanq3jvjcd.apps.googleusercontent.com
+ API_KEY
+ AIzaSyBFaSzvcvO8qg7El-2jRf7WctZIMKNA4-I
+ GCM_SENDER_ID
+ 521527250907
+ PLIST_VERSION
+ 1
+ BUNDLE_ID
+ com.example.travelMate
+ PROJECT_ID
+ travelmate-a47f5
+ STORAGE_BUCKET
+ travelmate-a47f5.firebasestorage.app
+ IS_ADS_ENABLED
+
+ IS_ANALYTICS_ENABLED
+
+ IS_APPINVITE_ENABLED
+
+ IS_GCM_ENABLED
+
+ IS_SIGNIN_ENABLED
+
+ GOOGLE_APP_ID
+ 1:521527250907:ios:64b41be39c54db1c7da1fe
+
+
\ No newline at end of file
diff --git a/lib/components/profile/profile_content.dart b/lib/components/profile/profile_content.dart
index 4431e52..4f589c6 100644
--- a/lib/components/profile/profile_content.dart
+++ b/lib/components/profile/profile_content.dart
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../../providers/user_provider.dart';
+import '../../services/auth_service.dart';
class ProfileContent extends StatelessWidget {
const ProfileContent({super.key});
@@ -143,12 +144,34 @@ class ProfileContent extends StatelessWidget {
child: Text('Annuler'),
),
TextButton(
- onPressed: () {
- // TODO: Implémenter la mise à jour
- Navigator.of(context).pop();
- ScaffoldMessenger.of(
- context,
- ).showSnackBar(SnackBar(content: Text('Profil mis à jour !')));
+ onPressed: () async {
+ if (prenomController.text.trim().isNotEmpty &&
+ nomController.text.trim().isNotEmpty) {
+ final updatedUser = user.copyWith(
+ nom: nomController.text.trim(),
+ prenom: prenomController.text.trim(),
+ );
+
+ final success = await Provider.of(context,
+ listen: false)
+ .updateUser(updatedUser);
+
+ Navigator.of(context).pop();
+
+ if (success) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(
+ content: Text('Profil mis à jour !'),
+ backgroundColor: Colors.green),
+ );
+ } else {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(
+ content: Text('Erreur lors de la mise à jour'),
+ backgroundColor: Colors.red),
+ );
+ }
+ }
},
child: Text('Sauvegarder'),
),
@@ -159,6 +182,11 @@ class ProfileContent extends StatelessWidget {
}
void _showChangePasswordDialog(BuildContext context) {
+ final currentPasswordController = TextEditingController();
+ final newPasswordController = TextEditingController();
+ final confirmPasswordController = TextEditingController();
+ final authService = AuthService();
+
showDialog(
context: context,
builder: (BuildContext context) {
@@ -168,16 +196,19 @@ class ProfileContent extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
children: [
TextField(
+ controller: currentPasswordController,
obscureText: true,
decoration: InputDecoration(labelText: 'Mot de passe actuel'),
),
SizedBox(height: 16),
TextField(
+ controller: newPasswordController,
obscureText: true,
decoration: InputDecoration(labelText: 'Nouveau mot de passe'),
),
SizedBox(height: 16),
TextField(
+ controller: confirmPasswordController,
obscureText: true,
decoration: InputDecoration(
labelText: 'Confirmer le mot de passe',
@@ -191,12 +222,59 @@ class ProfileContent extends StatelessWidget {
child: Text('Annuler'),
),
TextButton(
- onPressed: () {
- // TODO: Implémenter le changement de mot de passe
- Navigator.of(context).pop();
- ScaffoldMessenger.of(context).showSnackBar(
- SnackBar(content: Text('Mot de passe changé !')),
- );
+ onPressed: () async {
+ if (currentPasswordController.text.isEmpty ||
+ newPasswordController.text.isEmpty ||
+ confirmPasswordController.text.isEmpty) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(
+ content: Text('Tous les champs sont requis'),
+ backgroundColor: Colors.red),
+ );
+ return;
+ }
+
+ if (newPasswordController.text != confirmPasswordController.text) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(
+ content: Text('Les mots de passe ne correspondent pas'),
+ backgroundColor: Colors.red),
+ );
+ return;
+ }
+
+ if (newPasswordController.text.length < 8) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(
+ content:
+ Text('Le mot de passe doit contenir au moins 8 caractères'),
+ backgroundColor: Colors.red),
+ );
+ return;
+ }
+
+ try {
+ final user = Provider.of(context, listen: false)
+ .currentUser;
+ await authService.resetPasswordFromCurrentPassword(
+ currentPassword: currentPasswordController.text,
+ newPassword: newPasswordController.text,
+ email: user!.email,
+ );
+
+ Navigator.of(context).pop();
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(
+ content: Text('Mot de passe changé !'),
+ backgroundColor: Colors.green),
+ );
+ } catch (e) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(
+ content: Text('Erreur: Mot de passe actuel incorrect'),
+ backgroundColor: Colors.red),
+ );
+ }
},
child: Text('Changer'),
),
@@ -207,13 +285,30 @@ class ProfileContent extends StatelessWidget {
}
void _showDeleteAccountDialog(BuildContext context) {
+ final passwordController = TextEditingController();
+ final authService = AuthService();
+
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Supprimer le compte'),
- content: Text(
- 'Êtes-vous sûr de vouloir supprimer votre compte ? Cette action est irréversible.',
+ content: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Text(
+ 'Êtes-vous sûr de vouloir supprimer votre compte ? Cette action est irréversible.',
+ ),
+ SizedBox(height: 16),
+ TextField(
+ controller: passwordController,
+ obscureText: true,
+ decoration: InputDecoration(
+ labelText: 'Confirmez votre mot de passe',
+ border: OutlineInputBorder(),
+ ),
+ ),
+ ],
),
actions: [
TextButton(
@@ -221,15 +316,39 @@ class ProfileContent extends StatelessWidget {
child: Text('Annuler'),
),
TextButton(
- onPressed: () {
- Navigator.of(context).pop();
- // TODO: Implémenter la suppression
- Provider.of(context, listen: false).logout();
- Navigator.pushNamedAndRemoveUntil(
- context,
- '/login',
- (route) => false,
- );
+ onPressed: () async {
+ if (passwordController.text.isEmpty) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(
+ content: Text('Mot de passe requis'),
+ backgroundColor: Colors.red),
+ );
+ return;
+ }
+
+ try {
+ final user = Provider.of(context, listen: false)
+ .currentUser;
+ await authService.deleteAccount(
+ password: passwordController.text,
+ email: user!.email,
+ );
+
+ Navigator.of(context).pop();
+ Provider.of(context, listen: false).logout();
+ Navigator.pushNamedAndRemoveUntil(
+ context,
+ '/login',
+ (route) => false,
+ );
+ } catch (e) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(
+ content:
+ Text('Erreur lors de la suppression: Mot de passe incorrect'),
+ backgroundColor: Colors.red),
+ );
+ }
},
style: TextButton.styleFrom(foregroundColor: Colors.red),
child: Text('Supprimer'),
diff --git a/lib/firebase_options.dart b/lib/firebase_options.dart
new file mode 100644
index 0000000..9cede1f
--- /dev/null
+++ b/lib/firebase_options.dart
@@ -0,0 +1,77 @@
+// File generated by FlutterFire CLI.
+// ignore_for_file: type=lint
+import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
+import 'package:flutter/foundation.dart'
+ show defaultTargetPlatform, kIsWeb, TargetPlatform;
+
+/// Default [FirebaseOptions] for use with your Firebase apps.
+///
+/// Example:
+/// ```dart
+/// import 'firebase_options.dart';
+/// // ...
+/// await Firebase.initializeApp(
+/// options: DefaultFirebaseOptions.currentPlatform,
+/// );
+/// ```
+class DefaultFirebaseOptions {
+ static FirebaseOptions get currentPlatform {
+ if (kIsWeb) {
+ throw UnsupportedError(
+ 'DefaultFirebaseOptions have not been configured for web - '
+ 'you can reconfigure this by running the FlutterFire CLI again.',
+ );
+ }
+ switch (defaultTargetPlatform) {
+ case TargetPlatform.android:
+ return android;
+ case TargetPlatform.iOS:
+ return ios;
+ case TargetPlatform.macOS:
+ throw UnsupportedError(
+ 'DefaultFirebaseOptions have not been configured for macos - '
+ 'you can reconfigure this by running the FlutterFire CLI again.',
+ );
+ case TargetPlatform.windows:
+ return windows;
+ case TargetPlatform.linux:
+ throw UnsupportedError(
+ 'DefaultFirebaseOptions have not been configured for linux - '
+ 'you can reconfigure this by running the FlutterFire CLI again.',
+ );
+ default:
+ throw UnsupportedError(
+ 'DefaultFirebaseOptions are not supported for this platform.',
+ );
+ }
+ }
+
+ static const FirebaseOptions android = FirebaseOptions(
+ apiKey: 'AIzaSyAON_ol0Jr34tKbETvdDK9JCQdKNawxBeQ',
+ appId: '1:521527250907:android:be3db7fc84f053ec7da1fe',
+ messagingSenderId: '521527250907',
+ projectId: 'travelmate-a47f5',
+ storageBucket: 'travelmate-a47f5.firebasestorage.app',
+ );
+
+ static const FirebaseOptions ios = FirebaseOptions(
+ apiKey: 'AIzaSyBFaSzvcvO8qg7El-2jRf7WctZIMKNA4-I',
+ appId: '1:521527250907:ios:64b41be39c54db1c7da1fe',
+ messagingSenderId: '521527250907',
+ projectId: 'travelmate-a47f5',
+ storageBucket: 'travelmate-a47f5.firebasestorage.app',
+ iosClientId: '521527250907-3i1qe2656eojs8k9hjdi573j09i9p41m.apps.googleusercontent.com',
+ iosBundleId: 'com.example.travelMate',
+ );
+
+ static const FirebaseOptions windows = FirebaseOptions(
+ apiKey: 'AIzaSyC4t-WOvp22zns9b9t58urznsNAhSHRAag',
+ appId: '1:521527250907:web:53ff98bcdb8c218f7da1fe',
+ messagingSenderId: '521527250907',
+ projectId: 'travelmate-a47f5',
+ authDomain: 'travelmate-a47f5.firebaseapp.com',
+ storageBucket: 'travelmate-a47f5.firebasestorage.app',
+ measurementId: 'G-J246Y7J61M',
+ );
+
+}
\ No newline at end of file
diff --git a/lib/main.dart b/lib/main.dart
index 7ff439c..65e5e85 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -6,8 +6,14 @@ import 'pages/login.dart';
import 'pages/home.dart';
import 'providers/theme_provider.dart';
import 'providers/user_provider.dart';
+import 'package:firebase_core/firebase_core.dart';
+import 'firebase_options.dart';
-void main() {
+void main() async {
+ WidgetsFlutterBinding.ensureInitialized();
+ await Firebase.initializeApp(
+ options: DefaultFirebaseOptions.currentPlatform,
+);
runApp(
MultiProvider(
providers: [
diff --git a/lib/models/user.dart b/lib/models/user.dart
index 4d74e44..13ca0f0 100644
--- a/lib/models/user.dart
+++ b/lib/models/user.dart
@@ -5,14 +5,12 @@ class User {
final String nom;
final String prenom;
final String email;
- final String password;
User({
this.id,
required this.nom,
required this.prenom,
required this.email,
- required this.password,
});
// Constructeur pour créer un User depuis un Map (utile pour Firebase)
@@ -22,7 +20,6 @@ class User {
nom: map['nom'] ?? '',
prenom: map['prenom'] ?? '',
email: map['email'] ?? '',
- password: map['password'] ?? '',
);
}
@@ -39,7 +36,6 @@ class User {
'nom': nom,
'prenom': prenom,
'email': email,
- 'password': password,
};
}
@@ -57,14 +53,12 @@ class User {
String? nom,
String? prenom,
String? email,
- String? password,
}) {
return User(
id: id ?? this.id,
nom: nom ?? this.nom,
prenom: prenom ?? this.prenom,
email: email ?? this.email,
- password: password ?? this.password,
);
}
@@ -82,3 +76,4 @@ class User {
@override
int get hashCode => email.hashCode;
}
+
diff --git a/lib/pages/login.dart b/lib/pages/login.dart
index 31073be..a2bdd9c 100644
--- a/lib/pages/login.dart
+++ b/lib/pages/login.dart
@@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
-import '../services/user_service.dart';
+import '../services/auth_service.dart';
import 'package:provider/provider.dart';
import '../providers/user_provider.dart';
@@ -14,7 +14,7 @@ class _LoginPageState extends State {
final _formKey = GlobalKey();
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
- final _userService = UserService();
+ final _authService = AuthService();
bool _isLoading = false;
bool _obscurePassword = true;
@@ -26,26 +26,6 @@ class _LoginPageState extends State {
super.dispose();
}
- // Validation de l'email
- String? _validateEmail(String? value) {
- if (value == null || value.trim().isEmpty) {
- return 'Email requis';
- }
- final emailRegex = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$');
- if (!emailRegex.hasMatch(value.trim())) {
- return 'Email invalide';
- }
- return null;
- }
-
- // Validation du mot de passe
- String? _validatePassword(String? value) {
- if (value == null || value.isEmpty) {
- return 'Mot de passe requis';
- }
- return null;
- }
-
// Méthode de connexion
Future _login() async {
if (!_formKey.currentState!.validate()) {
@@ -57,24 +37,26 @@ class _LoginPageState extends State {
});
try {
- final user = await _userService.authenticateUser(
- _emailController.text.trim(),
- _passwordController.text,
+ final userCredential = await _authService.signIn(
+ email: _emailController.text.trim(),
+ password: _passwordController.text,
);
- if (mounted) {
- if (user != null) {
- // Naviguer vers la page d'accueil
- Provider.of(context, listen: false).setCurrentUser(user);
+ if (mounted && userCredential.user != null) {
+ // Récupérer les données utilisateur depuis Firestore
+ final userProvider = Provider.of(context, listen: false);
+ final userData = await userProvider.getUserData(userCredential.user!.uid);
+
+ if (userData != null) {
+ userProvider.setCurrentUser(userData);
Navigator.pushReplacementNamed(context, '/home');
} else {
- // Échec de la connexion
- _showErrorMessage('Email ou mot de passe incorrect');
+ _showErrorMessage('Données utilisateur non trouvées');
}
}
} catch (e) {
if (mounted) {
- _showErrorMessage('Erreur lors de la connexion: ${e.toString()}');
+ _showErrorMessage('Email ou mot de passe incorrect');
}
} finally {
if (mounted) {
@@ -85,18 +67,6 @@ class _LoginPageState extends State {
}
}
- void _showErrorMessage(String message) {
- if (mounted) {
- ScaffoldMessenger.of(context).showSnackBar(
- SnackBar(
- content: Text(message),
- backgroundColor: Colors.red,
- duration: const Duration(seconds: 3),
- ),
- );
- }
- }
-
@override
Widget build(BuildContext context) {
return Scaffold(
@@ -341,4 +311,36 @@ class _LoginPageState extends State {
),
);
}
+
+ // Validation de l'email
+ String? _validateEmail(String? value) {
+ if (value == null || value.trim().isEmpty) {
+ return 'Email requis';
+ }
+ final emailRegex = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$');
+ if (!emailRegex.hasMatch(value.trim())) {
+ return 'Email invalide';
+ }
+ return null;
+ }
+
+ // Validation du mot de passe
+ String? _validatePassword(String? value) {
+ if (value == null || value.isEmpty) {
+ return 'Mot de passe requis';
+ }
+ return null;
+ }
+
+ void _showErrorMessage(String message) {
+ if (mounted) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(
+ content: Text(message),
+ backgroundColor: Colors.red,
+ duration: const Duration(seconds: 3),
+ ),
+ );
+ }
+ }
}
diff --git a/lib/pages/signup.dart b/lib/pages/signup.dart
index 981686f..bc2e19f 100644
--- a/lib/pages/signup.dart
+++ b/lib/pages/signup.dart
@@ -1,7 +1,8 @@
import 'package:flutter/material.dart';
-import 'package:bcrypt/bcrypt.dart';
+import 'package:provider/provider.dart';
import '../models/user.dart';
-import '../services/user_service.dart';
+import '../services/auth_service.dart';
+import '../providers/user_provider.dart';
class SignUpPage extends StatefulWidget {
const SignUpPage({super.key});
@@ -17,7 +18,7 @@ class _SignUpPageState extends State {
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
final _confirmPasswordController = TextEditingController();
- final _userService = UserService();
+ final _authService = AuthService();
bool _isLoading = false;
bool _obscurePassword = true;
@@ -84,39 +85,29 @@ class _SignUpPageState extends State {
});
try {
- // Vérifier si l'email existe déjà
- bool emailExists = await _userService.emailExists(
- _emailController.text.trim(),
- );
- if (emailExists) {
- _showErrorDialog('Cet email est déjà utilisé');
- return;
- }
-
- // Hasher le mot de passe
- String hashedPassword = BCrypt.hashpw(
- _passwordController.text,
- BCrypt.gensalt(),
+ // Créer le compte avec Firebase Auth
+ final userCredential = await _authService.createAccount(
+ email: _emailController.text.trim(),
+ password: _passwordController.text,
);
- // Créer l'utilisateur
+ // Créer l'objet User
User newUser = User(
+ id: userCredential.user!.uid,
nom: _nomController.text.trim(),
prenom: _prenomController.text.trim(),
- email: _emailController.text.trim().toLowerCase(),
- password: hashedPassword,
+ email: _emailController.text.trim(),
);
- // Sauvegarder l'utilisateur
- bool success = await _userService.addUser(newUser);
+ // Sauvegarder les données utilisateur dans Firestore
+ await Provider.of(context, listen: false).saveUserData(newUser);
- if (success) {
- _showSuccessDialog();
- } else {
- _showErrorDialog('Erreur lors de la création du compte');
- }
+ // Mettre à jour le displayName
+ await _authService.updateDisplayName(displayName: newUser.fullName);
+
+ _showSuccessDialog();
} catch (e) {
- _showErrorDialog('Une erreur est survenue: ${e.toString()}');
+ _showErrorDialog('Erreur lors de la création du compte: ${e.toString()}');
} finally {
setState(() {
_isLoading = false;
@@ -351,3 +342,4 @@ class _SignUpPageState extends State {
);
}
}
+
\ No newline at end of file
diff --git a/lib/providers/user_provider.dart b/lib/providers/user_provider.dart
index 9a8b455..3161b89 100644
--- a/lib/providers/user_provider.dart
+++ b/lib/providers/user_provider.dart
@@ -1,8 +1,13 @@
import 'package:flutter/material.dart';
+import 'package:firebase_auth/firebase_auth.dart' as firebase_auth;
+import 'package:cloud_firestore/cloud_firestore.dart';
import '../models/user.dart';
+import '../services/auth_service.dart';
class UserProvider extends ChangeNotifier {
User? _currentUser;
+ final AuthService _authService = AuthService();
+ final FirebaseFirestore _firestore = FirebaseFirestore.instance;
User? get currentUser => _currentUser;
@@ -11,10 +16,65 @@ class UserProvider extends ChangeNotifier {
notifyListeners();
}
- void logout() {
+ Future logout() async {
+ await _authService.signOut();
_currentUser = null;
notifyListeners();
}
bool get isLoggedIn => _currentUser != null;
+
+ // Méthode pour récupérer les données utilisateur depuis Firestore
+ Future getUserData(String uid) async {
+ try {
+ DocumentSnapshot doc = await _firestore.collection('users').doc(uid).get();
+ if (doc.exists) {
+ Map data = doc.data() as Map;
+ return User.fromMap({...data, 'id': uid});
+ }
+ return null;
+ } catch (e) {
+ print('Erreur lors de la récupération des données utilisateur: $e');
+ return null;
+ }
+ }
+
+ // Méthode pour sauvegarder les données utilisateur dans Firestore
+ Future saveUserData(User user) async {
+ try {
+ await _firestore.collection('users').doc(user.id).set(user.toMap());
+ } catch (e) {
+ print('Erreur lors de la sauvegarde des données utilisateur: $e');
+ throw e;
+ }
+ }
+
+ // Méthode pour mettre à jour les données utilisateur
+ Future updateUser(User updatedUser) async {
+ try {
+ await _firestore.collection('users').doc(updatedUser.id).update(updatedUser.toMap());
+
+ // Mettre à jour le displayName dans Firebase Auth
+ await _authService.updateDisplayName(displayName: updatedUser.fullName);
+
+ _currentUser = updatedUser;
+ notifyListeners();
+ return true;
+ } catch (e) {
+ print('Erreur lors de la mise à jour de l\'utilisateur: $e');
+ return false;
+ }
+ }
+
+ // Initialiser l'utilisateur connecté
+ Future initializeUser() async {
+ firebase_auth.User? firebaseUser = _authService.currentUser;
+ if (firebaseUser != null) {
+ User? userData = await getUserData(firebaseUser.uid);
+ if (userData != null) {
+ _currentUser = userData;
+ notifyListeners();
+ }
+ }
+ }
}
diff --git a/lib/services/auth_service.dart b/lib/services/auth_service.dart
new file mode 100644
index 0000000..de5d11e
--- /dev/null
+++ b/lib/services/auth_service.dart
@@ -0,0 +1,68 @@
+import 'package:firebase_auth/firebase_auth.dart';
+
+class AuthService {
+ final FirebaseAuth firebaseAuth = FirebaseAuth.instance;
+
+ User? get currentUser => firebaseAuth.currentUser;
+
+ Stream get authStateChanges => firebaseAuth.authStateChanges();
+
+ Future signIn({
+ required String email,
+ required String password
+ }) async {
+ return await firebaseAuth.signInWithEmailAndPassword(
+ email: email, password: password);
+ }
+
+ Future createAccount({
+ required String email,
+ required String password
+ }) async {
+ return await firebaseAuth.createUserWithEmailAndPassword(
+ email: email, password: password);
+ }
+
+ Future signOut() async {
+ await firebaseAuth.signOut();
+ }
+
+ Future resetPassword(String email) async {
+ await firebaseAuth.sendPasswordResetEmail(email: email);
+ }
+
+ Future updateDisplayName({
+ required String displayName,
+ }) async {
+ await currentUser!.updateDisplayName(displayName);
+ }
+
+ Future deleteAccount({
+ required String password,
+ required String email,
+ }) async {
+ // Re-authenticate the user
+ AuthCredential credential =
+ EmailAuthProvider.credential(email: email, password: password);
+ await currentUser!.reauthenticateWithCredential(credential);
+
+ // Delete the user
+ await currentUser!.delete();
+ await firebaseAuth.signOut();
+ }
+
+ Future resetPasswordFromCurrentPassword({
+ required String currentPassword,
+ required String newPassword,
+ required String email,
+ }) async {
+ // Re-authenticate the user
+ AuthCredential credential =
+ EmailAuthProvider.credential(email: email, password: currentPassword);
+ await currentUser!.reauthenticateWithCredential(credential);
+
+ // Update the password
+ await currentUser!.updatePassword(newPassword);
+ }
+
+}
\ No newline at end of file
diff --git a/lib/services/user_service.dart b/lib/services/user_service.dart
deleted file mode 100644
index f85afcd..0000000
--- a/lib/services/user_service.dart
+++ /dev/null
@@ -1,272 +0,0 @@
-import 'dart:io';
-import 'dart:convert';
-import 'package:path_provider/path_provider.dart';
-import 'package:bcrypt/bcrypt.dart';
-import '../models/user.dart';
-
-class UserService {
- static const String _fileName = 'users.json';
-
- // Obtenir le fichier JSON
- Future _getUserFile() async {
- final directory = await getApplicationDocumentsDirectory();
- return File('${directory.path}/$_fileName');
- }
-
- // Charger tous les utilisateurs
- Future> loadUsers() async {
- try {
- final file = await _getUserFile();
- if (!await file.exists()) return [];
-
- final contents = await file.readAsString();
- if (contents.isEmpty) return [];
-
- final List jsonList = json.decode(contents);
-
- return jsonList.map((json) => User.fromMap(json)).toList();
- } catch (e) {
- //print('Erreur lors du chargement des utilisateurs: $e');
- return [];
- }
- }
-
- // Sauvegarder tous les utilisateurs
- Future saveUsers(List users) async {
- try {
- final file = await _getUserFile();
- final jsonList = users.map((user) => user.toMap()).toList();
- await file.writeAsString(json.encode(jsonList));
- } catch (e) {
- //print('Erreur lors de la sauvegarde des utilisateurs: $e');
- throw Exception('Erreur de sauvegarde');
- }
- }
-
- // Ajouter un nouvel utilisateur
- Future addUser(User user) async {
- try {
- final users = await loadUsers();
-
- // Vérifier si l'email existe déjà
- if (users.any((u) => u.email.toLowerCase() == user.email.toLowerCase())) {
- return false; // Email déjà utilisé
- }
-
- // Générer un ID unique
- final newUser = user.copyWith(
- id: DateTime.now().millisecondsSinceEpoch.toString(),
- );
-
- users.add(newUser);
- await saveUsers(users);
- return true;
- } catch (e) {
- //print('Erreur lors de l\'ajout de l\'utilisateur: $e');
- return false;
- }
- }
-
- // Authentifier un utilisateur avec bcrypt
- Future authenticateUser(String email, String password) async {
- try {
- final users = await loadUsers();
-
- // Trouver l'utilisateur par email (insensible à la casse)
- User? user;
- try {
- user = users.firstWhere(
- (u) => u.email.toLowerCase() == email.toLowerCase(),
- );
- } catch (e) {
- return null; // Utilisateur non trouvé
- }
-
- // Vérifier le mot de passe avec bcrypt
- if (BCrypt.checkpw(password, user.password)) {
- return user;
- }
-
- return null; // Mot de passe incorrect
- } catch (e) {
- //print('Erreur lors de l\'authentification: $e');
- return null;
- }
- }
-
- // Vérifier si un email existe
- Future emailExists(String email) async {
- try {
- final users = await loadUsers();
- return users.any(
- (user) => user.email.toLowerCase() == email.toLowerCase(),
- );
- } catch (e) {
- //print('Erreur lors de la vérification de l\'email: $e');
- return false;
- }
- }
-
- // Obtenir un utilisateur par ID
- Future getUserById(String id) async {
- try {
- final users = await loadUsers();
- return users.firstWhere((user) => user.id == id);
- } catch (e) {
- //print('Utilisateur avec l\'ID $id non trouvé');
- return null;
- }
- }
-
- // Obtenir un utilisateur par email
- Future getUserByEmail(String email) async {
- try {
- final users = await loadUsers();
- return users.firstWhere(
- (user) => user.email.toLowerCase() == email.toLowerCase(),
- );
- } catch (e) {
- //print('Utilisateur avec l\'email $email non trouvé');
- return null;
- }
- }
-
- // Mettre à jour un utilisateur
- Future updateUser(User updatedUser) async {
- try {
- final users = await loadUsers();
- final index = users.indexWhere((user) => user.id == updatedUser.id);
-
- if (index != -1) {
- users[index] = updatedUser;
- await saveUsers(users);
- return true;
- }
- return false; // Utilisateur non trouvé
- } catch (e) {
- //print('Erreur lors de la mise à jour de l\'utilisateur: $e');
- return false;
- }
- }
-
- // Supprimer un utilisateur
- Future deleteUser(String id) async {
- try {
- final users = await loadUsers();
- final initialLength = users.length;
- users.removeWhere((user) => user.id == id);
-
- if (users.length < initialLength) {
- await saveUsers(users);
- return true;
- }
- return false; // Utilisateur non trouvé
- } catch (e) {
- //print('Erreur lors de la suppression de l\'utilisateur: $e');
- return false;
- }
- }
-
- // Changer le mot de passe d'un utilisateur
- Future changePassword(
- String userId,
- String oldPassword,
- String newPassword,
- ) async {
- try {
- final users = await loadUsers();
- final userIndex = users.indexWhere((user) => user.id == userId);
-
- if (userIndex == -1) return false; // Utilisateur non trouvé
-
- final user = users[userIndex];
-
- // Vérifier l'ancien mot de passe
- if (!BCrypt.checkpw(oldPassword, user.password)) {
- return false; // Ancien mot de passe incorrect
- }
-
- // Hasher le nouveau mot de passe
- final hashedNewPassword = BCrypt.hashpw(newPassword, BCrypt.gensalt());
-
- // Mettre à jour l'utilisateur
- users[userIndex] = user.copyWith(password: hashedNewPassword);
- await saveUsers(users);
-
- return true;
- } catch (e) {
- //print('Erreur lors du changement de mot de passe: $e');
- return false;
- }
- }
-
- // Réinitialiser le mot de passe (pour la fonctionnalité "mot de passe oublié")
- Future resetPassword(String email, String newPassword) async {
- try {
- final users = await loadUsers();
- final userIndex = users.indexWhere(
- (user) => user.email.toLowerCase() == email.toLowerCase(),
- );
-
- if (userIndex == -1) return false; // Utilisateur non trouvé
-
- // Hasher le nouveau mot de passe
- final hashedNewPassword = BCrypt.hashpw(newPassword, BCrypt.gensalt());
-
- // Mettre à jour l'utilisateur
- users[userIndex] = users[userIndex].copyWith(password: hashedNewPassword);
- await saveUsers(users);
-
- return true;
- } catch (e) {
- //print('Erreur lors de la réinitialisation du mot de passe: $e');
- return false;
- }
- }
-
- // Obtenir le nombre total d'utilisateurs
- Future getUserCount() async {
- try {
- final users = await loadUsers();
- return users.length;
- } catch (e) {
- //print('Erreur lors du comptage des utilisateurs: $e');
- return 0;
- }
- }
-
- // Vider la base de données (utile pour les tests)
- Future clearAllUsers() async {
- try {
- await saveUsers([]);
- } catch (e) {
- //print('Erreur lors du vidage de la base de données: $e');
- }
- }
-
- // Méthode pour créer des utilisateurs de test
- Future createTestUsers() async {
- try {
- final testUsers = [
- User(
- nom: 'Dupont',
- prenom: 'Jean',
- email: 'jean.dupont@test.com',
- password: BCrypt.hashpw('password123', BCrypt.gensalt()),
- ),
- User(
- nom: 'Martin',
- prenom: 'Marie',
- email: 'marie.martin@test.com',
- password: BCrypt.hashpw('password123', BCrypt.gensalt()),
- ),
- ];
-
- for (final user in testUsers) {
- await addUser(user);
- }
- } catch (e) {
- //print('Erreur lors de la création des utilisateurs de test: $e');
- }
- }
-}
diff --git a/pubspec.lock b/pubspec.lock
index 22e2dbf..7b06651 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -1,6 +1,14 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
+ _flutterfire_internals:
+ dependency: transitive
+ description:
+ name: _flutterfire_internals
+ sha256: "23d16f00a2da8ffa997c782453c73867b0609bd90435195671a54de38a3566df"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.3.62"
async:
dependency: transitive
description:
@@ -41,6 +49,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.2"
+ cloud_firestore:
+ dependency: "direct main"
+ description:
+ name: cloud_firestore
+ sha256: af66aeffe5943d582c0f655ec860433dbd773ac4a4ffc62c11960b52a18833fe
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.0.2"
+ cloud_firestore_platform_interface:
+ dependency: transitive
+ description:
+ name: cloud_firestore_platform_interface
+ sha256: "494dd3d275a0259e3ba08b442b54e64839b0cf58352a50fe97eb67cacf3bad28"
+ url: "https://pub.dev"
+ source: hosted
+ version: "7.0.2"
+ cloud_firestore_web:
+ dependency: transitive
+ description:
+ name: cloud_firestore_web
+ sha256: "85b7b071c23eecbbbb47a9fbd2cfdb1b6af20f2323663fd90234d91a064f0584"
+ url: "https://pub.dev"
+ source: hosted
+ version: "5.0.2"
collection:
dependency: transitive
description:
@@ -89,6 +121,54 @@ packages:
url: "https://pub.dev"
source: hosted
version: "7.0.1"
+ firebase_auth:
+ dependency: "direct main"
+ description:
+ name: firebase_auth
+ sha256: "735f857c9363376eeb585e7ba57e67e5f495202cd3f609902b8769795fd823bc"
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.1.0"
+ firebase_auth_platform_interface:
+ dependency: transitive
+ description:
+ name: firebase_auth_platform_interface
+ sha256: "5badda0ea5048ffbb1726169cf5530539490de8055c3bd43f4f9cd5fcef8e556"
+ url: "https://pub.dev"
+ source: hosted
+ version: "8.1.2"
+ firebase_auth_web:
+ dependency: transitive
+ description:
+ name: firebase_auth_web
+ sha256: "07c889d2c56e648ed30225e819801d7e45542747a658d9c385520de35d312dec"
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.0.3"
+ firebase_core:
+ dependency: "direct main"
+ description:
+ name: firebase_core
+ sha256: "4dd96f05015c0dcceaa47711394c32971aee70169625d5e2477e7676c01ce0ee"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.1.1"
+ firebase_core_platform_interface:
+ dependency: transitive
+ description:
+ name: firebase_core_platform_interface
+ sha256: "5873a370f0d232918e23a5a6137dbe4c2c47cf017301f4ea02d9d636e52f60f0"
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.0.1"
+ firebase_core_web:
+ dependency: transitive
+ description:
+ name: firebase_core_web
+ sha256: "61a51037312dac781f713308903bb7a1762a7f92f7bc286a3a0947fb2a713b82"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.1.1"
flutter:
dependency: "direct main"
description: flutter
@@ -176,6 +256,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.15.6"
+ http:
+ dependency: transitive
+ description:
+ name: http
+ sha256: bb2ce4590bc2667c96f318d68cac1b5a7987ec819351d32b1c987239a815e007
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.5.0"
http_parser:
dependency: transitive
description:
diff --git a/pubspec.yaml b/pubspec.yaml
index 87cca5c..5c87b6d 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -40,6 +40,9 @@ dependencies:
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.8
google_maps_flutter: ^2.5.0
+ firebase_core: ^4.1.1
+ firebase_auth: ^6.1.0
+ cloud_firestore: ^6.0.2
dev_dependencies:
flutter_test: