
Gad Ntenta
Développeur Full Stack
Firebase avec Flutter
FlutterFirebaseBackendAuthentication
Intégrez Firebase dans votre application Flutter pour l'authentification, la base de données et plus encore.
Firebase avec Flutter
Introduction
Firebase est une plateforme complète qui offre de nombreux services pour le développement d'applications. Dans cet article, nous allons explorer comment intégrer et utiliser Firebase dans vos applications Flutter.
Configuration de Firebase
1. Installation des Dépendances
Dans votre pubspec.yaml :
dependencies:
firebase_core: ^2.15.0
firebase_auth: ^4.7.2
cloud_firestore: ^4.8.4
firebase_storage: ^11.2.5
firebase_messaging: ^14.6.5
2. Initialisation de Firebase
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
runApp(MyApp());
}
Authentification
1. Service d'Authentification
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
// État de l'authentification
Stream<User?> get authStateChanges => _auth.authStateChanges();
// Connexion avec email et mot de passe
Future<UserCredential> signInWithEmailAndPassword(
String email,
String password,
) async {
try {
return await _auth.signInWithEmailAndPassword(
email: email,
password: password,
);
} on FirebaseAuthException catch (e) {
throw _handleAuthException(e);
}
}
// Inscription avec email et mot de passe
Future<UserCredential> createUserWithEmailAndPassword(
String email,
String password,
) async {
try {
return await _auth.createUserWithEmailAndPassword(
email: email,
password: password,
);
} on FirebaseAuthException catch (e) {
throw _handleAuthException(e);
}
}
// Déconnexion
Future<void> signOut() async {
await _auth.signOut();
}
// Gestion des erreurs
Exception _handleAuthException(FirebaseAuthException e) {
switch (e.code) {
case 'user-not-found':
return Exception('Aucun utilisateur trouvé avec cet email.');
case 'wrong-password':
return Exception('Mot de passe incorrect.');
case 'email-already-in-use':
return Exception('Cet email est déjà utilisé.');
default:
return Exception('Une erreur est survenue.');
}
}
}
2. Interface de Connexion
class LoginScreen extends StatefulWidget {
@override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final _formKey = GlobalKey<FormState>();
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
final _authService = AuthService();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Connexion')),
body: Form(
key: _formKey,
child: Padding(
padding: EdgeInsets.all(16.0),
child: Column(
children: [
TextFormField(
controller: _emailController,
decoration: InputDecoration(labelText: 'Email'),
validator: (value) {
if (value?.isEmpty ?? true) {
return 'Veuillez entrer votre email';
}
return null;
},
),
TextFormField(
controller: _passwordController,
decoration: InputDecoration(labelText: 'Mot de passe'),
obscureText: true,
validator: (value) {
if (value?.isEmpty ?? true) {
return 'Veuillez entrer votre mot de passe';
}
return null;
},
),
ElevatedButton(
onPressed: _signIn,
child: Text('Se connecter'),
),
],
),
),
),
);
}
Future<void> _signIn() async {
if (_formKey.currentState?.validate() ?? false) {
try {
await _authService.signInWithEmailAndPassword(
_emailController.text,
_passwordController.text,
);
Navigator.pushReplacementNamed(context, '/home');
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(e.toString())),
);
}
}
}
}
Firestore
1. Service de Base de Données
class FirestoreService {
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
// Collection des utilisateurs
CollectionReference get users => _firestore.collection('users');
// Ajouter un utilisateur
Future<void> addUser(UserModel user) async {
try {
await users.doc(user.id).set(user.toMap());
} catch (e) {
throw Exception('Erreur lors de l'ajout de l'utilisateur');
}
}
// Récupérer un utilisateur
Future<UserModel?> getUser(String id) async {
try {
final doc = await users.doc(id).get();
if (doc.exists) {
return UserModel.fromMap(doc.data() as Map<String, dynamic>);
}
return null;
} catch (e) {
throw Exception('Erreur lors de la récupération de l'utilisateur');
}
}
// Mettre à jour un utilisateur
Future<void> updateUser(String id, Map<String, dynamic> data) async {
try {
await users.doc(id).update(data);
} catch (e) {
throw Exception('Erreur lors de la mise à jour de l'utilisateur');
}
}
// Supprimer un utilisateur
Future<void> deleteUser(String id) async {
try {
await users.doc(id).delete();
} catch (e) {
throw Exception('Erreur lors de la suppression de l'utilisateur');
}
}
}
2. Modèle de Données
class UserModel {
final String id;
final String name;
final String email;
final DateTime createdAt;
UserModel({
required this.id,
required this.name,
required this.email,
required this.createdAt,
});
Map<String, dynamic> toMap() {
return {
'id': id,
'name': name,
'email': email,
'createdAt': createdAt.toIso8601String(),
};
}
factory UserModel.fromMap(Map<String, dynamic> map) {
return UserModel(
id: map['id'],
name: map['name'],
email: map['email'],
createdAt: DateTime.parse(map['createdAt']),
);
}
}
Stockage
1. Service de Stockage
class StorageService {
final FirebaseStorage _storage = FirebaseStorage.instance;
// Référence au dossier des images
Reference get imagesRef => _storage.ref().child('images');
// Uploader une image
Future<String> uploadImage(File file, String userId) async {
try {
final ref = imagesRef.child('$userId/${DateTime.now()}.jpg');
await ref.putFile(file);
return await ref.getDownloadURL();
} catch (e) {
throw Exception('Erreur lors de l'upload de l'image');
}
}
// Supprimer une image
Future<void> deleteImage(String url) async {
try {
final ref = _storage.refFromURL(url);
await ref.delete();
} catch (e) {
throw Exception('Erreur lors de la suppression de l'image');
}
}
}
2. Interface de Téléchargement
class ImageUploadWidget extends StatefulWidget {
@override
_ImageUploadWidgetState createState() => _ImageUploadWidgetState();
}
class _ImageUploadWidgetState extends State<ImageUploadWidget> {
final _storageService = StorageService();
File? _image;
bool _isUploading = false;
Future<void> _pickImage() async {
final picker = ImagePicker();
final pickedFile = await picker.pickImage(source: ImageSource.gallery);
if (pickedFile != null) {
setState(() {
_image = File(pickedFile.path);
});
}
}
Future<void> _uploadImage() async {
if (_image == null) return;
setState(() {
_isUploading = true;
});
try {
final url = await _storageService.uploadImage(
_image!,
FirebaseAuth.instance.currentUser!.uid,
);
// Mettre à jour le profil utilisateur avec l'URL de l'image
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(e.toString())),
);
} finally {
setState(() {
_isUploading = false;
});
}
}
@override
Widget build(BuildContext context) {
return Column(
children: [
if (_image != null) ...[
Image.file(_image!, height: 200),
SizedBox(height: 16),
],
ElevatedButton(
onPressed: _isUploading ? null : _pickImage,
child: Text('Choisir une image'),
),
if (_image != null)
ElevatedButton(
onPressed: _isUploading ? null : _uploadImage,
child: _isUploading
? CircularProgressIndicator()
: Text('Télécharger'),
),
],
);
}
}
Notifications Push
1. Configuration des Notifications
class NotificationService {
final FirebaseMessaging _messaging = FirebaseMessaging.instance;
Future<void> initialize() async {
// Demander la permission
NotificationSettings settings = await _messaging.requestPermission(
alert: true,
badge: true,
sound: true,
);
if (settings.authorizationStatus == AuthorizationStatus.authorized) {
// Obtenir le token
String? token = await _messaging.getToken();
print('FCM Token: $token');
// Configurer les handlers
FirebaseMessaging.onMessage.listen(_handleMessage);
FirebaseMessaging.onMessageOpenedApp.listen(_handleMessageOpenedApp);
}
}
void _handleMessage(RemoteMessage message) {
print('Message reçu: ${message.notification?.title}');
// Afficher une notification locale
}
void _handleMessageOpenedApp(RemoteMessage message) {
print('Message ouvert: ${message.notification?.title}');
// Naviguer vers la page appropriée
}
}
2. Utilisation dans l'Application
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
final notificationService = NotificationService();
await notificationService.initialize();
runApp(MyApp());
}
Meilleures Pratiques
1. Sécurité
- Utilisez les règles de sécurité Firestore
- Validez les données côté serveur
- Gérez les tokens de manière sécurisée
- Utilisez l'authentification à deux facteurs
2. Performance
- Mettez en cache les données
- Utilisez la pagination
- Optimisez les requêtes
- Gérer la connexion internet
3. Tests
- Testez les services Firebase
- Testez l'authentification
- Testez les règles de sécurité
- Testez les notifications
Conclusion
Firebase offre une suite complète d'outils pour le développement d'applications Flutter. En suivant ces bonnes pratiques et en utilisant les services appropriés, vous pouvez créer des applications robustes et évolutives.
N'oubliez pas de :
- Sécuriser vos données
- Optimiser les performances
- Tester vos implémentations
- Suivre les mises à jour de Firebase
Avec ces connaissances, vous êtes prêt à intégrer Firebase dans vos applications Flutter !