La base de données en temps réel de Firebase
Sommaire
- 1- Objectif
- 2- Présentation
- 3- Les principales capacités de la base de données en temps réel de firebase
- 4- Comparaison entre
Cloud FirestoreetRealtime Database - 5- Configuration et installation de bases de données en temps réel
- 5.1- Configuration du projet Firebase
- 5.2- Génération d'un fichier de configuration de projet Firebase:
- 5.3- Installation de votre fichier de configuration Firebase :
- 5.4- Ajout de dépendances Firebase :
- 5.5- Initialisation de la base de données en temps réel Firebase :
- 6- Lecture dans la base de données en temps réel de Firebase
- 6.1- Initialisation de la base de données
- 6.1.1- Initialisation de Firebase
- 6.1.2- Créer une référence à la base de données
- 6.2- Lecture dans la base de données
- 6.2.1- Lire une seule fois (get)
- 6.2.2- Écouter en temps réel (onValue)
- 6.2.3- Lire une liste de données
- 7- Écriture dans la base de données en temps réel de Firebase
- 7.1- Modélisation avec une classe
Livre - 7.2- Initialisation de la base de données
- 7.2.1- Initialisation de Firebase
- 7.2.2- Créer une référence à la base de données
- 7.3- Écriture dans la base de données
- 7.3.1- Écrire une simple valeur
- 7.3.2- Écrire un objet (Map)
- 7.3.3- Ajouter un nouvel élément avec une clé générée automatiquement (
push()) - 7.4- Structure résultante dans la base de données
- 8- Modification d’une information dans Firebase Realtime Database
- 8.1- Modifier une valeur existante
- 8.1.1- Modifier une valeur simple avec
set() - 8.1.2- Modifier un seul champ dans un objet avec
update() - 8.1.3- Modifier plusieurs champs en même temps
- 8.1.4- Modification atomique de plusieurs chemins (multi-location update)
- 8.1.5- Modifier une entrée à clé générée automatiquement
- 8.1.6- Modifier un compteur en toute sécurité avec
runTransaction() - 9- Exemple Flutter complet : CRUD avec Firebase Realtime Database
- 9.1- Ajouter les dépendances dans
pubspec.yaml - 9.2- Initialiser Firebase dans
main.dart - 9.3- Créer la page firebaseService
firebase_service.dart - 9.4- Créer la page principale
home_page.dart - 9.4.1- Cours Flutter
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 :
- 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 .
- En résumé
- 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. -
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 :
-
Ajout de dépendances Firebase :
- Ouvrez votre projet Flutter et accédez au fichier
pubspec.yaml. - Ajoutez les dépendances suivantes sous la dependenciessection :
- Exécutez flutter pub getpour récupérer les dépendances nouvellement ajoutées.
-
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 :
-
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 :
-
Créer une référence à la base de données
-
Lecture dans la base de données
-
Lire une seule fois (get)
-
Écouter en temps réel (onValue)
-
Lire une liste de données
-
É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.
-
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 :
-
Créer une référence à la base de données
-
Écriture dans la base de données
-
Écrire une simple valeur
-
Écrire un objet (Map)
-
Ajouter un nouvel élément avec une clé générée automatiquement (
push()) -
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) : -
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.-
Modifier un seul champ dans un objet avec
update() update()ne modifie que le champ cité, sans toucher aux autres.-
Modifier plusieurs champs en même temps
-
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).
-
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. -
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.
-
Exemple Flutter complet : CRUD avec Firebase Realtime Database
-
Ajouter les dépendances dans
pubspec.yaml -
Initialiser Firebase dans
main.dart -
Créer la page firebaseService
firebase_service.dart -
Créer la page principale
home_page.dart
Cloud Firestore
| 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 |


plugins {
id("com.google.gms.google-services")
}
Donc :
En Groovy ➔ tu faisapply plugin: '...'
En Kotlin DSL ➔ tu faisplugins { id("...") }
dépendances :
flutter :
sdk : flutter
firebase_core : ^1.4.0
firebase_database : ^11.0.0
// 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());
}
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(),
);
}
}
// 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');
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é.
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.
void readList() async {
final snapshot = await database.child('utilisateurs').get(); if (snapshot.exists) { Map utilisateurs = 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.
class Livre {
final String titre;
final String auteur;
final int annee;
Livre({required this.titre, required this.auteur, required this.annee});
Map toMap() {
return {
'titre': titre,
'auteur': auteur,
'annee': annee,
};
}
factory Livre.fromMap(Map data) {
return Livre(
titre: data['titre'],
auteur: data['auteur'],
annee: data['annee'],
);
}
}
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());
}
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');
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à).
void writeObject() async {
try {
Map utilisateur = {
'nomUtilisateur': 'Jean',
'email': 'jean@example.com',
};
await database.child('utilisateurs').child('userId123').set(utilisateur);
} catch (e) {
print("Erreur lors de l'écriture : $e");
}
}
void pushNewObject() async {
try {
Map utilisateur = {
'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.
{
"utilisateurs": {
"userId123": {
"nomUtilisateur": "Jean",
"email": "jean@example.com"
}
}
}
{
"utilisateurs": {
"-NyZt12a3BcD4567Ef": {
"nomUtilisateur": "Sami",
"email": "sami@example.com"
}
}
}
void updateSimpleValue() {
database.child('message').set('Bonjour modifié !');
}
void updateUserEmail() {
database.child('utilisateurs').child('userId123').update({
'email': 'nouveauemail@example.com',
});
}
void updateUserData() {
database.child('utilisateurs').child('userId123').update({
'nomUtilisateur': 'Jean Modifié',
'email': 'jeanmodifie@example.com',
});
}
void updateMultipleLocations() {
final updates = {
'/utilisateurs/userId123/email': 'newemail@example.com',
'/messages/messageId456/contenu': 'Message modifié',
};
database.update(updates);
}
// Supposons que la clé est "cleAuto123"
void updateAutoKeyUser() {
database.child('utilisateurs').child('cleAuto123').update({
'nomUtilisateur': 'Sami Modifié',
});
}
void incrementVisitCount() {
final countRef = database.child('visites');
countRef.runTransaction((currentData) {
return Transaction.success((currentData ?? 0) + 1); // Incrémente de 1
});
}
dependencies:
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.5
import '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
);
}
}
// 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;
}
}
import '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();
},
),
],
);
},
);
}
}
