Ce guide est conçu pour les débutants. Nous allons créer une application de gestion de tâches où les données sont stockées dans le cloud (Cloud Firestore) et synchronisées en temps réel sur tous les appareils.
Prérequis
Avant de commencer, assurez-vous d'avoir :
- Le SDK Flutter installé et configuré.
- Un éditeur de code (VS Code ou Android Studio).
- Un compte Google pour accéder à la console Firebase.
- Node.js installé (pour utiliser la ligne de commande Firebase).
Étape 1 : Création du projet Flutter
Ouvrez votre terminal et créez un nouveau projet :
flutter create ma_super_todo
cd ma_super_todo
Étape 2 : Configuration de Firebase (La méthode moderne)
Nous allons utiliser FlutterFire CLI, la méthode recommandée par Google.
Installez les outils Firebase (si ce n'est pas déjà fait) :
npm install -g firebase-tools
Connectez-vous à votre compte Google :
firebase login
Activez le CLI FlutterFire :
dart pub global activate flutterfire_cli
Configurez le projet :Dans le dossier de votre projet Flutter (ma_super_todo), lancez :
flutterfire configure
- Sélectionnez "Create a new project".
- Donnez-lui un nom (ex:
ma-super-todo-db). - Sélectionnez les plateformes (Android, iOS, Web).
Cela va générer automatiquement un fichier firebase_options.dart dans votre dossier lib. C'est la clé de liaison entre votre app et Firebase.
Étape 3 : Configuration de la Base de Données (Console Firebase)
- Allez sur la Console Firebase.
- Sélectionnez votre projet nouvellement créé.
- Dans le menu de gauche, cliquez sur Build > Firestore Database.
- Cliquez sur Créer une base de données.
- Choisissez un emplacement (ex:
eur3pour l'Europe ounam5pour les USA).
Important pour le développement : Choisissez "Démarrer en mode test".
- Note : Cela permet d'écrire/lire sans authentification pendant 30 jours. Pour une vraie app, il faudra sécuriser les règles plus tard.
Étape 4 : Installation des dépendances
Ouvrez votre terminal dans le dossier du projet et ajoutez les paquets nécessaires :
flutter pub add firebase_core cloud_firestore
firebase_core: Le cœur de Firebase.cloud_firestore: Pour interagir avec la base de données.
Étape 5 : Le Code - Initialisation
Ouvrez lib/main.dart. Nous devons initialiser Firebase avant de lancer l'application.
Remplacez tout le contenu de main.dart par ceci :
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'firebase_options.dart'; // Généré automatiquement par flutterfire configure
void main() async {
// 1. On s'assure que les widgets sont prêts
WidgetsFlutterBinding.ensureInitialized();
// 2. On initialise Firebase avec les options de notre projet
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Ma Super Todo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.deepPurple,
useMaterial3: true,
),
home: const TodoListPage(),
);
}
}
Étape 6 : L'interface et la Logique (TodoListPage)
À la suite du code précédent (ou dans un nouveau fichier), créez la page principale. C'est ici que toute la magie opère.
Nous allons utiliser un StreamBuilder. C'est un widget puissant qui écoute la base de données en temps réel. Si vous ajoutez une tâche depuis la console Firebase, elle apparaîtra instantanément dans l'app sans recharger !
class TodoListPage extends StatefulWidget {
const TodoListPage({super.key});
@override
State<TodoListPage> createState() => _TodoListPageState();
}
class _TodoListPageState extends State<TodoListPage> {
// Contrôleur pour récupérer le texte saisi
final TextEditingController _taskController = TextEditingController();
// --- FONCTION : Ajouter une tâche ---
void _addTask() {
if (_taskController.text.isEmpty) return;
// On ajoute un document dans la collection 'todos'
FirebaseFirestore.instance.collection('todos').add({
'title': _taskController.text, // Le titre de la tâche
'isDone': false, // Par défaut, pas terminée
'createdAt': Timestamp.now(), // Pour trier par date
});
_taskController.clear(); // On vide le champ de texte
Navigator.of(context).pop(); // On ferme la fenêtre de dialogue
}
// --- FONCTION : Supprimer une tâche ---
void _deleteTask(String docId) {
FirebaseFirestore.instance.collection('todos').doc(docId).delete();
}
// --- FONCTION : Basculer l'état (Fait / Pas fait) ---
void _toggleTask(String docId, bool currentStatus) {
FirebaseFirestore.instance.collection('todos').doc(docId).update({
'isDone': !currentStatus,
});
}
// --- UI : Fenêtre pour ajouter une tâche ---
void _showAddDialog() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Nouvelle tâche'),
content: TextField(
controller: _taskController,
decoration: const InputDecoration(hintText: "Ex: Acheter du pain"),
autofocus: true,
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Annuler'),
),
ElevatedButton(
onPressed: _addTask,
child: const Text('Ajouter'),
),
],
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Mes Tâches Firebase'),
backgroundColor: Colors.deepPurple.shade100,
),
// Le bouton flottant pour ajouter
floatingActionButton: FloatingActionButton(
onPressed: _showAddDialog,
child: const Icon(Icons.add),
),
// Le corps de la page : StreamBuilder
body: StreamBuilder<QuerySnapshot>(
// On écoute la collection 'todos' triée par date
stream: FirebaseFirestore.instance
.collection('todos')
.orderBy('createdAt', descending: true)
.snapshots(),
builder: (context, snapshot) {
// Cas 1 : Erreur
if (snapshot.hasError) {
return const Center(child: Text('Une erreur est survenue'));
}
// Cas 2 : Chargement
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
// Cas 3 : Liste vide
if (snapshot.data!.docs.isEmpty) {
return const Center(child: Text('Aucune tâche pour le moment !'));
}
// Cas 4 : Affichage de la liste
final docs = snapshot.data!.docs;
return ListView.builder(
itemCount: docs.length,
itemBuilder: (context, index) {
// On récupère les données du document
final doc = docs[index];
final data = doc.data() as Map<String, dynamic>;
final String docId = doc.id; // L'ID unique généré par Firestore
return Card(
margin: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
child: ListTile(
// Checkbox pour marquer comme fait
leading: Checkbox(
value: data['isDone'] ?? false,
onChanged: (val) => _toggleTask(docId, data['isDone']),
),
// Le titre de la tâche (barré si fini)
title: Text(
data['title'] ?? 'Sans titre',
style: TextStyle(
decoration: (data['isDone'] ?? false)
? TextDecoration.lineThrough
: null,
color: (data['isDone'] ?? false) ? Colors.grey : Colors.black,
),
),
// Bouton supprimer
trailing: IconButton(
icon: const Icon(Icons.delete, color: Colors.redAccent),
onPressed: () => _deleteTask(docId),
),
),
);
},
);
},
),
);
}
}
Étape 7 : Lancer l'application
- Assurez-vous que votre émulateur est lancé ou que votre téléphone est branché.
Exécutez la commande :
flutter run
Ce que vous devriez voir :
- Une liste vide au début.
- Un bouton "+" qui ouvre une fenêtre.
- Quand vous ajoutez une tâche, elle apparaît instantanément.
- Si vous allez dans votre console Firebase (dans le navigateur), vous verrez les données apparaître dans "Firestore Database" en temps réel.
- Si vous cochez la case, le champ
isDonepasse àtruedans la base de données.
Résumé des concepts clés appris
- FirebaseFirestore.instance : Le point d'entrée pour parler à la base de données.
- collection('...').add(...) : Créer une nouvelle donnée (Create).
- snapshots() + StreamBuilder : La méthode magique pour lire les données et mettre à jour l'écran automatiquement quand la base de données change (Read).
- doc(id).update(...) : Modifier une donnée existante (Update).
- doc(id).delete() : Supprimer une donnée (Delete).
Félicitations ! Vous venez de créer votre première application avec Flutter.