La base de données en temps réel de Firebase
Objectif
- Firebase Real-time Database
Présentation
- La base de données temps réel Firebase est une base de données hébergée dans le cloud, stockant les données au format JSON. Les données sont synchronisées en temps réel avec chaque client connecté. Tous nos clients partagent une instance de base de données temps réel et reçoivent automatiquement les mises à jour avec les données les plus récentes lorsque nous développons des applications multiplateformes avec nos SDK iOS et JavaScript.
- La base de données Firebase Realtime est une base de données NoSQL permettant de stocker et de synchroniser les données entre nos utilisateurs en temps réel.
- Il s'agit d'un objet JSON volumineux que les développeurs peuvent gérer en temps réel. Grâce à une API unique, la base de données Firebase fournit à l'application la valeur actuelle des données et leurs mises à jour. La synchronisation en temps réel permet à nos utilisateurs d'accéder facilement à leurs données depuis n'importe quel appareil, web ou mobile.
- La base de données en temps réel facilite la collaboration entre nos utilisateurs. Elle est fournie avec des SDK mobiles et web, qui nous permettent de développer notre application sans serveur. Lorsque nos utilisateurs sont hors ligne, les SDK de la base de données en temps réel utilisent le cache local de l'appareil pour traiter et stocker les modifications. Les données locales sont automatiquement synchronisées dès la connexion de l'appareil.
Les principales capacités de la base de données en temps réel de firebase
- Une base de données en temps réel est capable de fournir tous les services, en ligne et hors ligne. Ces fonctionnalités incluent l'accessibilité depuis l'appareil client, la mise à l'échelle sur plusieurs bases de données, et bien plus encore.
- En temps réel
- La base de données en temps réel Firebase utilise la synchronisation des données plutôt que les requêtes HTTP. Tout appareil connecté reçoit les mises à jour en quelques millisecondes. Elle ne prend pas en compte le code réseau et offre des expériences collaboratives et immersives.
- Hors ligne
- Le SDK de base de données Firebase conserve nos données sur le disque, ce qui permet aux applications Firebase de rester réactives même hors ligne. L'appareil client reçoit les modifications manquantes une fois la connexion rétablie.
- Accessible depuis les appareils clients
- Aucun serveur d'application n'est nécessaire pour accéder à la base de données Firebase Real-Time. Nous pouvons y accéder directement depuis un appareil mobile ou un navigateur web. La validation et la sécurité des données sont assurées par les règles de sécurité Firebase Real-Time Database, des règles basées sur des expressions exécutées lors de la lecture ou de l'écriture des données.
- Mise à l'échelle sur plusieurs bases de données
- Grâce à l'offre Firebase Real-Time Database sur Blaze, nous pouvons répondre aux besoins en données de notre application en répartissant nos données sur plusieurs instances de base de données au sein d'un même projet Firebase.
- L'authentification est simplifiée grâce à Firebase Authentication sur notre projet et les utilisateurs de nos instances de base de données sont authentifiés.
- L'accès aux données de chaque base de données est contrôlé grâce à des règles Firebase Real-Time personnalisées, disponibles pour chaque instance.
Comparaison entre
Cloud FirestoreetRealtime Database- Google vous offre la possibilité d'utiliser deux bases de données synchronisées en temps réel au sein de la suite d'outils Firebase : Cloud Firestore et Firebase Realtime Database . Mais quelle est la différence ?
Firestoreest la toute dernière solution proposée par Google. Conçue pour mieux gérer les logiciels à grande échelle avec des données fortement structurées, Firestore permet d'interroger et de renvoyer chaque document de données séparément, créant ainsi un environnement véritablement élastique qui s'adapte parfaitement à la croissance de votre ensemble de données.- Bien que
Realtime Databasesoit une base de données NoSQL orientée documents, elle renvoie les données au format JSON. Lorsqu'on interroge une arborescence de données JSON, tous ses nœuds enfants sont également renvoyés. Pour garantir des transactions rapides et efficaces, il est essentiel de maintenir une hiérarchie de données aussi plate que possible. - Ces deux solutions de bases de données sont excellentes ; il est donc important de savoir quand utiliser l’une ou l’autre. Voici quelques indicateurs clés pour chaque base de données : Cloud Firestore
- Il propose une formule gratuite, mais facture chaque transaction et, dans une moindre mesure, le stockage utilisé au-delà de la limite.
- Plus facile à adapter.
- Peut gérer des ensembles de données et des relations complexes et fortement structurés.
- Disponible sur mobile et sur le web.
- Base de données en temps réel Firebase
- Il existe également une formule gratuite, mais elle facture le stockage utilisé, et non les requêtes effectuées, au-delà de la limite.
- Latence extrêmement faible.
- Il est facile de stocker des données simples au format JSON.
- Disponible uniquement sur mobile.
- Lors de la création d'une application de messagerie instantanée, vous effectuerez de nombreuses transactions et stockerez des données simples sans avoir à réaliser de requêtes complexes. La base de données en temps réel est le choix idéal : elle est moins chère, plus facile à utiliser et plus rapide à exécuter.
- Consultez la liste complète des comparaisons dans la table suivante .
Critère Cloud Firestore Realtime Database Structure des données Base de données NoSQL basée sur des documents → Collections ⟶ Documents (similaires à JSON) Base de données NoSQL en temps réel basée sur un arbre JSON unique Échelle (scalabilité) Très scalable horizontalement grâce à l’architecture multi-régions Scalable mais peut devenir complexe à gérer si l'arborescence JSON devient trop profonde Synchronisation temps réel Oui, mais optimisée pour des cas semi-temps-réel Oui, synchronisation temps réel ultra-rapide par défaut Requêtes avancées Oui : filtres, tri, requêtes composées, pagination Très limitées : filtres simples (ex : orderByChild) Tarification Basée sur le nombre de lectures/écritures et stockage → peut coûter moins cher Basé sur la bande passante, le stockage et les téléchargements Performance hors-ligne Supporte bien l’offline, synchronise automatiquement une fois connecté Disponible mais nécessite une config spécifique (surtout Web) Sécurité Règles de sécurité granulaires basées sur les documents et champs Règles basées sur l’arbre JSON, souvent plus complexes à maintenir Cas d’usage idéal Applications structurées, évolutives, avec des requêtes flexibles (ex : e-commerce, réseaux sociaux) Chat, jeux multijoueurs, suivi de présence, tableau de bord temps réel
- Firestore est recommandé pour les applications modernes, structurées, ayant besoin de flexibilité dans les filtres et une évolutivité importante.
- Realtime Database est idéal pour des cas très exigeants en temps réel pur (ex. : chat instantané ou IoT), mais devient moins pratique pour des données complexes ou fortement liées.
Configuration et installation de bases de données en temps réel
-
Configuration du projet Firebase
- Accédez à la console Firebase et créez un nouveau projet.
- Avant de pouvoir utiliser l'un des services cloud de Google, vous devez configurer un projet sur la console Firebase . Ensuite, vous pouvez créer votre base de données en temps réel et la gérer directement depuis la console.
- Accédez à console.firebase.google.com et connectez-vous à l'aide de votre compte Gmail.
- Cliquez sur le bouton "créer un projet" puis sur le bouton continuer.
- Cliquez sur "Ajouter Firebase à votre application Flutter" et suivez les instructions d'installation. Vous obtiendrez ainsi un fichier
google-services.jsonà placer dans le répertoireandroid/app de votre projet Flutter.


- Accédez à la console Firebase et créez un nouveau projet.
-
Génération d'un fichier de configuration de projet Firebase:
- Sur la console Firebase , ajoutez une nouvelle application Android ou sélectionnez une application Android existante pour votre projet Firebase.
- Le "
Nom du package Android" doit correspondre au nom du package de votre projet local, créé au démarrage du projet Flutter. - Le nom du package actuel se trouve dans votre fichier Gradle de module (au niveau de l'application), généralement
android/app/build.gradledans la sectiondefaultConfig(exemple de nom de package : com.yourcompany.yourproject). - Une fois votre application Android enregistrée, téléchargez le fichier de configuration depuis la console Firebase (fichier nommé
google-services.json). Ajoutez ce fichier au répertoire de android/app votre projet Flutter.
-
Installation de votre fichier de configuration Firebase :
- Pour permettre à Firebase d'utiliser la configuration sur Android, le plugin "google-services" doit être appliqué au projet. Cela nécessite la modification de deux fichiers du android/répertoire.
- Tout d’abord, ajoutez le plugin "
google-services" en tant que dépendance à l’intérieur du fichier :android/build.gradlebuildscript { dependencies { // ... other dependencies classpath 'com.google.gms:google-services:4.3.8' } } - Enfin, exécutez le plugin en ajoutant ce qui suit sous la ligne apply plugin: '
com.android.application', dans le fichier :/android/app/build.gradlebuildscript { apply plugin: 'com.google.gms.google-services' - Si ton projet utilisait Gradle Kotlin DSL, les fichiers seraient en .gradle.kts (extension .kts pour Kotlin Script), et la syntaxe serait différente, par exemple :
plugins { id("com.google.gms.google-services") }Donc : En Groovy ➔ tu fais
apply plugin: '...'En Kotlin DSL ➔ tu faisplugins { id("...") }
-
Ajout de dépendances Firebase :
- Ouvrez votre projet Flutter et accédez au fichier
pubspec.yaml. - Ajoutez les dépendances suivantes sous la dependenciessection :
dépendances : flutter : sdk : flutter firebase_core : ^1.4.0 firebase_database : ^11.0.0 - Exécutez flutter pub getpour récupérer les dépendances nouvellement ajoutées.
- Ouvrez votre projet Flutter et accédez au fichier
-
Initialisation de la base de données en temps réel Firebase :
- Ouvrez le fichier
main.dartdans votre projet Flutter. - Importez les packages Firebase et Flutter nécessaires :
// Importation des bibliothèques nécessaires // Fournit les outils pour construire l'interface utilisateur Flutter import 'package:flutter/material.dart'; // Permet l'initialisation de Firebase dans l'application import 'package:firebase_core/firebase_core.dart'; // Permet d'interagir avec la base de données Realtime de Firebase import 'package:firebase_database/firebase_database.dart'; // Fonction principale de l'application, point d'entrée du programme void main() async { // S'assure que le moteur Flutter est prêt avant toute opération asynchrone WidgetsFlutterBinding.ensureInitialized(); // Initialise Firebase avec les paramètres par défaut (configuration définie dans firebase_options ou google-services.json) await Firebase.initializeApp(); // Lance l'application Flutter en affichant le widget MyApp runApp(MyApp()); }
- Ouvrez le fichier
Lecture dans la base de données en temps réel de Firebase
Initialisation de la base de données
- Pour lire depuis Firebase Realtime Database, nous devons d'abord obtenir une instance de la base de données, puis référencer l'emplacement que nous souhaitons écouter.
- Mode temps réel:
onValue(équivalent de.snapshots()dans Firestore) - Lecture ponctuelle :
get()(équivalent de.get())
Initialisation de Firebase
- Dans lib/main.dart:
import 'package:flutter/material.dart'; import 'package:firebase_core/firebase_core.dart'; import 'home_page.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Firebase Realtime Example', theme: ThemeData(primarySwatch: Colors.blue), home: HomePage(), ); } }
- Dans lib/main.dart:
Créer une référence à la base de données
// Référence à la racine de la base de données final DatabaseReference _database = FirebaseDatabase.instance.ref(); // Référence à un nœud spécifique (ex: "messages") final DatabaseReference _messageRef = _database.child('messages');
Lecture dans la base de données
Lire une seule fois (get)
import 'package:firebase_database/firebase_database.dart'; final database = FirebaseDatabase.instance.ref(); // Exemple de lecture unique void readOnce() async { final snapshot = await database.child('message').get(); if (snapshot.exists) { print('Valeur : ${snapshot.value}'); } else { print('Pas de données disponibles.'); } } // get() lit une seule fois la donnée au chemin donné.Écouter en temps réel (onValue)
void listenToChanges() { database.child('message').onValue.listen((DatabaseEvent event) { final data = event.snapshot.value; print('Nouveau message: $data'); }); } // onValue écoute toutes les modifications de la donnée en temps réel.Lire une liste de données
void readList() async { final snapshot = await database.child('utilisateurs').get(); if (snapshot.exists) { Maputilisateurs = snapshot.value as Map ; utilisateurs.forEach((key, value) { print('Utilisateur $key: ${value['nomUtilisateur']}, ${value['email']}'); }); } } // Lecture d'une collection complète sous forme de Map.
Écriture dans la base de données en temps réel de Firebase
Modélisation avec une classe
Livre- Même logique qu’avec Firestore — vous utilisez une classe pour représenter vos données.
class Livre { final String titre; final String auteur; final int annee; Livre({required this.titre, required this.auteur, required this.annee}); MaptoMap() { return { 'titre': titre, 'auteur': auteur, 'annee': annee, }; } factory Livre.fromMap(Map data) { return Livre( titre: data['titre'], auteur: data['auteur'], annee: data['annee'], ); } } Initialisation de la base de données
- Pour interagir avec Firebase Realtime Database, nous devons d'abord initialiser Firebase et créer une référence à notre base de données.
Initialisation de Firebase
- Dans lib/main.dart:
import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/material.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); // Nécessaire pour l'initialisation await Firebase.initializeApp(); // Initialise Firebase runApp(MyApp()); }
- Dans lib/main.dart:
Créer une référence à la base de données
import 'package:firebase_database/firebase_database.dart'; // Référence à la racine de la base de données final DatabaseReference _database = FirebaseDatabase.instance.ref(); // Référence à un nœud spécifique (ex: "messages") final DatabaseReference _messageRef = _database.child('messages');
Écriture dans la base de données
Écrire une simple valeur
import 'package:firebase_database/firebase_database.dart'; final database = FirebaseDatabase.instance.ref(); void writeSimpleValue() async { try { await database.child('message').set('Bonjour le monde!'); } catch (e) { print("Erreur lors de l'écriture : $e"); } } // set() sert à écrire une valeur (écrase si quelque chose existe déjà).Écrire un objet (Map)
void writeObject() async { try { Maputilisateur = { 'nomUtilisateur': 'Jean', 'email': 'jean@example.com', }; await database.child('utilisateurs').child('userId123').set(utilisateur); } catch (e) { print("Erreur lors de l'écriture : $e"); } } Ajouter un nouvel élément avec une clé générée automatiquement (
push())void pushNewObject() async { try { Maputilisateur = { 'nomUtilisateur': 'Sami', 'email': 'sami@example.com', }; await database.child('utilisateurs').push().set(utilisateur); } catch (e) { print("Erreur lors de l'ajout : $e"); } } // push() crée une clé unique automatiquement.
Structure résultante dans la base de données
- Avec
set()(remplace la valeur à un emplacement donné) : - Avec
push()(génère une clé unique automatiquement) :
{ "utilisateurs": { "userId123": { "nomUtilisateur": "Jean", "email": "jean@example.com" } } }{ "utilisateurs": { "-NyZt12a3BcD4567Ef": { "nomUtilisateur": "Sami", "email": "sami@example.com" } } }- Avec
Modification d’une information dans Firebase Realtime Database
Modifier une valeur existante
- Dans Firebase Realtime Database, une modification consiste à changer une donnée existante. Cela peut se faire avec :
set(): remplace complètement la valeur ou l'objetupdate(): modifie un ou plusieurs champs sans écraser tout l'objetpush().set()– ajout d’une entrée modifiablerunTransaction(): pour modifier des données sensibles aux conditions actuelles
Modifier une valeur simple avec
set()set()remplace complètement le contenu du nœudmessage.
void updateSimpleValue() { database.child('message').set('Bonjour modifié !'); }Modifier un seul champ dans un objet avec
update()update()ne modifie que le champ cité, sans toucher aux autres.
void updateUserEmail() { database.child('utilisateurs').child('userId123').update({ 'email': 'nouveauemail@example.com', }); }Modifier plusieurs champs en même temps
void updateUserData() { database.child('utilisateurs').child('userId123').update({ 'nomUtilisateur': 'Jean Modifié', 'email': 'jeanmodifie@example.com', }); }Modification atomique de plusieurs chemins (multi-location update)
- Cette approche permet de mettre à jour plusieurs emplacements différents avec une seule requête atomique (évite les incohérences).
void updateMultipleLocations() { final updates = { '/utilisateurs/userId123/email': 'newemail@example.com', '/messages/messageId456/contenu': 'Message modifié', }; database.update(updates); }Modifier une entrée à clé générée automatiquement
- Lorsqu’un objet est ajouté via
push(), la clé est générée automatiquement. Il est nécessaire de la connaître pour la modifier.
// Supposons que la clé est "cleAuto123" void updateAutoKeyUser() { database.child('utilisateurs').child('cleAuto123').update({ 'nomUtilisateur': 'Sami Modifié', }); }- Lorsqu’un objet est ajouté via
Modifier un compteur en toute sécurité avec
runTransaction()runTransaction()permet de modifier une donnée en tenant compte de sa valeur actuelle — utile pour des compteurs, likes, etc.- Cette méthode garantit que l’opération est effectuée même si plusieurs utilisateurs modifient la même valeur en même temps.
void incrementVisitCount() { final countRef = database.child('visites'); countRef.runTransaction((currentData) { return Transaction.success((currentData ?? 0) + 1); // Incrémente de 1 }); }
- Dans Firebase Realtime Database, une modification consiste à changer une donnée existante. Cela peut se faire avec :
Exemple Flutter complet : CRUD avec Firebase Realtime Database
Ajouter les dépendances dans
pubspec.yamldependencies: flutter: sdk: flutter # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.8 firebase_core: ^3.13.0 firebase_database: ^11.3.5Initialiser Firebase dans
main.dartimport 'package:flutter/material.dart'; import 'package:firebase_core/firebase_core.dart'; // Pour initialiser Firebase import 'home_page.dart'; // Importation de la page d'accueil void main() async { // S'assurer que Firebase est initialisé avant de lancer l'application WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); // Initialisation de Firebase runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Firebase Realtime Example', theme: ThemeData(primarySwatch: Colors.blue), home: HomePage(), // Page affichée au lancement de l'application ); } }Créer la page firebaseService
firebase_service.dart// firebase_service.dart import 'package:firebase_database/firebase_database.dart'; class FirebaseService { final DatabaseReference _dbRef = FirebaseDatabase.instance.ref().child('messages'); DatabaseReference get ref => _dbRef; Future<void> writeMessage(String message) async { await _dbRef.push().set({'text': message}); } Future<void> deleteMessage(String key) async { await _dbRef.child(key).remove(); } Future<void> updateMessage(String key, String newText) async { await _dbRef.child(key).update({'text': newText}); } Stream<DatabaseEvent> getMessagesStream() { return _dbRef.onValue; } }Créer la page principale
home_page.dartimport 'package:flutter/material.dart'; import 'package:firebase_database/firebase_database.dart'; import 'firebase_service.dart'; class HomePage extends StatefulWidget { @override _HomePageState createState() => _HomePageState(); } class _HomePageState extends State<HomePage> { final TextEditingController _controller = TextEditingController(); final FirebaseService _firebaseService = FirebaseService(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Firebase Realtime Database')), body: Padding( padding: const EdgeInsets.all(20.0), child: Column( children: [ TextField( controller: _controller, decoration: InputDecoration(labelText: 'Entrez un message'), ), SizedBox(height: 10), ElevatedButton( onPressed: _writeData, child: Text('Écrire dans Firebase'), ), SizedBox(height: 20), Expanded(child: _buildMessageList()), ], ), ), ); } void _writeData() { String message = _controller.text.trim(); if (message.isNotEmpty) { _firebaseService.writeMessage(message); _controller.clear(); } } Widget _buildMessageList() { return StreamBuilder<DatabaseEvent>( stream: _firebaseService.getMessagesStream(), builder: (context, snapshot) { if (snapshot.hasData && snapshot.data!.snapshot.value != null) { Map<dynamic, dynamic> messages = snapshot.data!.snapshot.value as Map<dynamic, dynamic>; List<MapEntry<dynamic, dynamic>> entries = messages.entries.toList().reversed.toList(); return ListView.builder( itemCount: entries.length, itemBuilder: (context, index) { String key = entries[index].key; String text = entries[index].value['text'] ?? ''; return ListTile( title: Text(text), trailing: PopupMenuButton<String>( onSelected: (value) { if (value == 'modifier') { _editMessageDialog(key, text); } else if (value == 'supprimer') { _firebaseService.deleteMessage(key); } }, itemBuilder: (context) => [ PopupMenuItem(value: 'modifier', child: Text('Modifier')), PopupMenuItem(value: 'supprimer', child: Text('Supprimer')), ], ), ); }, ); } else { return Center(child: Text('Aucun message trouvé.')); } }, ); } void _editMessageDialog(String key, String currentText) { TextEditingController editController = TextEditingController(text: currentText); showDialog( context: context, builder: (context) { return AlertDialog( title: Text('Modifier le message'), content: TextField( controller: editController, decoration: InputDecoration(labelText: 'Nouveau message'), ), actions: [ TextButton( child: Text('Annuler'), onPressed: () => Navigator.of(context).pop(), ), ElevatedButton( child: Text('Enregistrer'), onPressed: () { String newText = editController.text.trim(); if (newText.isNotEmpty) { _firebaseService.updateMessage(key, newText); } Navigator.of(context).pop(); }, ), ], ); }, ); } }
Ressource PDF disponible
Ce cours contient une version PDF téléchargeable. Le lien sera disponible prochainement.