Bonjour à tous et bienvenue dans ce premier tutoriel Flutter et Firebase français disponible sur le blog.
J’ai créé une nouvelle série de vidéos de tutos gratuits sur Flutter et Firebase pour montrer à un maximum de monde le potentiel de ses deux outils.
Je commence donc avec un premier tutoriel sur la configuration de Firebase et la création d’une petite base de données Firestore rapide.
On va créer une petite application simple pour récupérer les données de notre base Firestore et les afficher ou les modifier depuis Flutter.
Pour créer cette application, nous allons suivre les étapes suivantes:
- Configurer Firebase avec Flutter sur iOS et Android
- Créer notre première base de données Firestore
- Ajouter et mettre à jour les données depuis Flutter
Vous pouvez également télécharger le code source final de l’application en cliquant sur le bouton ci-dessous:
Flutter Firebase Français 1. Configurer Firebase avec Flutter sur iOS et Android
Avant de travailler sur notre base de données Firestore, nous devons créer un projet Firebase et nous y connecter depuis Flutter.
Nous verrons de manière détaillée la configuration de Firebase pour iOS et Android, qui demande quelques étapes différentes.
1.1. Créer un projet Firebase
On commence donc par créer un nouveau projet Firebase que nous allons par la suite associer à notre application Flutter.
Vous pouvez pour cela vous rendre sur le site officiel de Firebase: https://firebase.google.com/
Si vous avez déjà un compte, vous pouvez rendre directement dans votre console Firebase: https://console.firebase.google.com/u/0/
Une fois rendu dans votre console, cliquez sur le bouton “Ajouter un projet” pour créer un nouveau projet.
Donnez ensuite un nom à votre projet Firebase, comme le nom de votre application ou un nom de test comme “Firebase Flutter”:
Vous pourrez toujours créer d’autres projets Firebase par la suite, ici il s’agit juste d’un projet test.
Je vous invite à ne pas activer Google Analytics pour le moment, car c’est un outil plus avancé qui demande de nombreuses configurations.
Vous pouvez donc directement cliquer sur “Create project” pour lancer la création de votre projet Firebase:
Vous devriez donc maintenant arriver dans votre tableau de bord Firebase qui donne accès à de nombreux outils.
Encore une fois, Firebase est aujourd’hui très large qui permet de faire bien plus que de gérer des bases de données.
Pour faire simple, tous les services de backend imaginables pour mobiles sont disponibles dans Firebase (base de données, authentification, stockage, notification, publicité, analyse, machine learning, etc).
Dans ces leçons Firebase, nous nous contenteront de prendre en main les outils indispensables, à commencer aujourd’hui par la configuration.
On passe donc maintenant au code Dart qui nous permettra de nous connecter à Firebase depuis Flutter.
Pour cela nous allons utiliser le package firebase_core dont je vous laisse repérer la dernière version: https://pub.dev/packages/firebase_core
Vous pouvez ajouter les dépendances de ce package dans votre fichier Yaml:
firebase_core: ^1.12.0
Entrez ensuite la commande suivante pour le télécharger dans votre application:
flutter pub get
Vous pouvez maintenant importer ce package dans votre code Dart et initialiser votre projet Firebase comme ceci:
import 'package:firebase_core/firebase_core.dart'; Future<void> main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); runApp(const MyApp()); }
Voilà donc la base de notre application Flutter pour le moment, qui n'est pas encore suffisant tant que nous n'avons pas ajouté les fichiers de configuration iOS et Android.
1.2. Configurer Firebase sur iOS
Pour configurer Firebase sur votre application iOS Flutter, cela se résume à télécharger un fichier et à le placer dans le répertoire de votre application.
Je vous ai également ajouté quelques étapes supplémentaires pour optimiser votre code et éviter les principaux bogues liés à Firebase.
Mais retenez qu’il s’agit de télécharger le fichier GoogleService-Info.plist depuis Firebase et à le placer dans le sous dossier "ios/Runner/".
Pour commencer, nous allons vérifier le Bundle ID de votre application iOS Flutter, qui a été généré automatiquement par Flutter.
Le Bundle ID est l’identifiant unique d’une application iOS que le développeur ou l’entreprise choisit.
Par exemple, si vous avez créé une application Flutter que vous appelez “firebase“, votre bundle ID par défaut sera “com.example.firebase“.
Pour vérifier sa valeur, rendez-vous dans le dossier “ios/” de votre application Flutter et ouvrez le fichier Runner.xcodeproj:
Il s’agit d’un fichier Xcode qui vous donne accès à la version iOS de votre application Flutter.
Une fois ouvert, vous devriez voir apparaître les réglages généraux de votre application.
Celui qui nous intéresse s’appelle ici Bundle Identifier:
Comme j’ai nommé mon application “firebase” en la créant, Flutter a généré l’identifiant “com.example.firebase“.
Vous pouvez modifier cet identifiant par la suite pour l’adapter à votre projet d’application en utilisant la syntaxe suivante:
extension.nom_de_domaine.nom_de_votre_application
Par exemple mon application Miracle possède comme Bundle ID com.drissas.miracle.
Mon application Driss AS possède elle l’identifiant com.drissas.app.
Bref, pour le moment nous allons conserver le Bundle ID généré par défaut et l’entrer dans le champ iOS bundle ID:
Entrez ensuite le nom de votre application ou par exemple pour notre test “Test Firebase iOS“.
Pour l’instant laissez le champ App Store ID vide, puisque votre application n’est pas encore sur l’App Store.
Cliquez sur “Register App” pour enregistrer votre application iOS et téléchargez maintenant le fichier qu’il vous propose.
Il s’agit du fichier GoogleService-Info.plist et qui contient tous vos codes de configuration Firebase.
C’est lui qui va identifier votre application Flutter iOS à votre projet Firebase.
Vous pouvez donc placer ce fichier dans le sous dossier ios/Runner/ de votre application Flutter.
Attention toutefois, il existe un bug courant associé à ce fichier GoogleService-Info.plist.
Ouvrez à nouveau votre fichier Runner.xcodeproj pour lancer Xcode et vérifier la présence du fichier de configuration.
S’il n’apparaît pas, faites-le tout simplement glisser dans Xcode dans le dossier Runner qui apparaît dans le menu latéral.
Vous devriez maintenant voir votre fichier GoogleService-Info.plist depuis Xcode, on termine donc avec les derniers réglages et modifications à apporter à notre application.
Vous pouvez maintenant vous rendre dans le fichier Podfile qui vous permet de gérer les dépendances de votre projet Xcode.
Ce fichier se trouve dans le répertoire ios de votre application Flutter:
ios/Podfile
Tout en haut de votre fichier, enlevez le symbole ‘#’ de la deuxième ligne pour la décommenter et changer la version cible:
# Uncomment this line to define a global platform for your project platform :ios, '12.0'
Pour utiliser les dernières versions des packages Firebase de Flutter, nous sommes dans l’obligation de travailler avec les dernières versions d’iOS.
Je vous invite donc à cibler la version iOS 12.0 directement pour éviter tout bogue lié à l’exécution de votre code.
La deuxième partie de votre fichier à modifier nous sert à améliorer le temps de construction de notre application pour iOS.
C’est en effet un problème rencontré couramment qui concerne le temps d’intégration du SDK Firestore iOS à Xcode.
Flutter nous invite donc à utiliser une version recompilée en ajoutant cette ligne à votre Podfile:
pod 'FirebaseFirestore', :git => 'https://github.com/invertase/firestore-ios-sdk-frameworks.git', :tag => '8.11.0'
En ce qui concerne la version à cibler, rendez-vous sur le site suivant: https://cocoapods.org/pods/FirebaseFirestore
Repérez alors la dernière version de la dépendance FirebaseFirestore:
Ajoutez cette ligne dans votre bloc cible « Runner » dans votre fichier Podfile:
# ... target 'Runner' do pod 'FirebaseFirestore', :git => 'https://github.com/invertase/firestore-ios-sdk-frameworks.git', :tag => '8.11.0' # ... end
De plus, assurez-vous d’avoir mis à niveau vos CocoaPods vers la version 1.9.1 ou supérieure:
gem install cocoapods
Votre application Flutter pourra maintenant se construire beaucoup plus rapidement et sans encombre dans un émulateur iOS.
1.3. Configurer Firebase sur Android
Passons maintenant à la configuration de Firebase sur Android qui demande aussi quelques paramétrages et codes supplémentaires.
Pour commencer nous avons besoin nom du package Android de votre application, vous pouvez le trouver dans le fichier XML suivant de votre application Flutter:
android/app/src/main/AndroidManifest.xml
Dans la première balise <manifest> vous trouverez sa valeur dans l’attribut package:XML
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.firebase">
Si vous avez suivant la configuration iOS, ce nom de package sera le même que votre Bundle ID.
Une fois donc que vous l’avez récupéré, entrez-le dans le champ “Android package name” puis entrez le nom de votre application:
Vous n’avez pas besoin de remplir le champ SHA-1 pour le moment, il sert pour des fonctionnalités plus avancées.
Cliquez ensuite sur “Register app” pour enregistrer votre application et générer le fichier de configuration.
Téléchargez-le ensuite dans votre sous dossier android/app/ de votre application Flutter:
Pour Android, pas besoin de toucher à nouveau à ce fichier, aucun bogue ne lui est associé.
En revanche, il est recommandé de suivre certaines des étapes suivantes, pour commencer, rendez-vous dans le fichier:
android/app/build.gradle
Et venez modifier la configuration minimale de votre SDK Android à 21 (certains plugins Firebase ne fonctionnent pas avec les versions inférieures):
defaultConfig { ... minSdkVersion 21 ... }
Ensuite et toujours dans le même fichier, vous pouvez ajouter la ligne de code suivante à la fin de votre fichier:
apply plugin: 'com.google.gms.google-services'
Cela vous donnera accès au plugin Android Google Services Gradle qui vous permettra de lire votre fichier de configuration Firebase.
Pour finir, rendez-vous dans le deuxième fichier Android à modifier:
android/build.gradle
Et ajoutez la nouvelle dépendance de votre plugin dans la balise buildscript:
buildscript { repositories { // ... } dependencies { // ... classpath 'com.google.gms:google-services:4.3.3' } }
Ici nous travaillons avec du code lié au développement avec Android Studio classique.
Nous ne nous attarderons pas sur les syntaxes et méthodes de développement d’Android qui sont différentes de Flutter.
À part ce genre de petit réglage, vous n’aurez pas besoin de toucher au code Android de votre application Flutter.
Pour finir et être sûr que notre configuration Firebase Android fonctionnera, nous pouvons ouvrir Android Studio.
Ouvrez directement le répertoire android de votre application Flutter dans Android Studio:
La synchronisation du Gradle devrait automatiquement se mettre en route et des fenêtres pop-up devraient apparaître pour vous proposer de nouveaux réglages.
Acceptez toutes les propositions d’Android Studio et autres mises à jour.
Vous pouvez aussi lancer la synchronisation manuellement en cliquant sur le bouton éléphant dans votre menu en haut à droite:
Une fois la synchronisation terminée, le message “Gradle sync finished” devrait apparaître dans la console d’Android Studio:
Voilà pour la configuration de Firebase pour Android qui demande effectivement un peu plus de travail.
Flutter Firebase Français 2. Créer sa première base de données Firestore
Votre application Flutter est maintenant officiellement connectée à votre projet Firebase, grâce à toutes les opérations que nous venons de mener.
Nous allons donc passer à la création de notre première base de données Cloud Firestore pour découvrir le service phare de Firebase.
Vous pouvez accéder à ce service depuis votre console Firebase dans le menu latéral:
Nous allons commencer par créer manuellement notre base de données en créant une Collection.
Une collection représente l'équivalent d'une table de donnée dans Firestore.
Vous pouvez donc créer une première collection de film que vous appelez "Movies":
Vous allez ensuite pouvoir créer votre premier Document, c'est-à-dire élément de votre table.
Ici chaque document représentera un film dans notre collection Firestore.
Vous pouvez générer aléatoirement un ID pour ce document et ajouter deux champs pour commencer.
Il s'agit pour le moment de champs de type chaine de caractère qui nous permettent de stocker le nom du film et son affiche.
Nous verrons bientôt comment les afficher dans notre application Flutter.
À termes, chaque document de film contiendra les informations suivantes pour proposer un profil plus complet:
Maintenant que notre collection Firestore est créée, nous allons passer à la récupération des données dans Flutter.
Pour cela, vous allez avoir besoin du package Cloud Firestore: https://pub.dev/packages/cloud_firestore
Vous pouvez déjà ajouter sa dépendance dans votre fichier pubspec.yaml:
cloud_firestore: ^3.1.8
Je vous laisse aussi la documentation très complète de FlutterFire: https://firebase.flutter.dev/docs/firestore/usage/
Nous allons par exemple reprendre l'exemple de récupération en temps réel des données: https://firebase.flutter.dev/docs/firestore/usage/#realtime-changes
Le principe, c'est que Firebase va nous renvoyer un flux de données qui se mettront à jour en temps réel.
On sera capable de récupérer un flux de données issu d'une collection ou d'un document particulier avec la syntaxe suivante:
Stream collectionStream = FirebaseFirestore.instance.collection('users').snapshots(); Stream documentStream = FirebaseFirestore.instance.collection('users').doc('ABC123').snapshots();
On peut aussi reprendre le widget préconçu qu'il propose en l'adaptant à notre thématique, ici les films et notre collection 'Movies':
class MoviesInformation extends StatefulWidget { const MoviesInformation({Key? key}) : super(key: key); @override _MoviesInformationState createState() => _MoviesInformationState(); } class _MoviesInformationState extends State<MoviesInformation> { final Stream<QuerySnapshot> _moviesStream = FirebaseFirestore.instance.collection('Movies').snapshots(); @override Widget build(BuildContext context) { return StreamBuilder<QuerySnapshot>( stream: _moviesStream, builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) { if (snapshot.hasError) { return Text('Something went wrong'); } if (snapshot.connectionState == ConnectionState.waiting) { return Text("Loading"); } return ListView( children: snapshot.data!.docs.map((DocumentSnapshot document) { Map<String, dynamic> movie = document.data()! as Map<String, dynamic>; return ListTile( title: Text(movie['name']), subtitle: Text(movie['poster']), ); }).toList(), ); }, ); } }
On commence tout d'abord par récupérer notre flux de données issu de notre collection 'Movies':
final Stream<QuerySnapshot> _moviesStream = FirebaseFirestore.instance.collection('Movies').snapshots();
On utilise ensuite le widget StreamBuilder() pour construire notre contenu à partir de ce flux:
StreamBuilder<QuerySnapshot>( stream: _moviesStream, builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) { [...] }, );
Vous pouvez donc reprendre votre fichier main.dart pour créer les widgets suivants et positionner votre widget MoviesInformation() dans votre application:
import 'package:flutter/material.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; Future<void> main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, theme: ThemeData.dark(), title: 'Flutter Firebase', home: const HomePage(), ); } } class HomePage extends StatelessWidget { const HomePage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Flutter Firebase'), body: const SingleChildScrollView( child: MoviesInformation(), ), ); } }
Vous êtes donc maintenant capable d'afficher des données d'une base Firestore dans votre application Flutter.
Passons maintenant à un exercice un peu plus avancé pour ajouter nous-même des données et les mettre à jour en direct depuis Flutter.
Flutter Firebase Français 3. Ajouter et mettre à jour les données depuis Flutter
Dans cette dernière partie, nous allons voir comment manipuler des données un peu plus complexes dans Firestore.
Nous allons permettre à l'utilisateur d'ajouter un film depuis l'application et de mettre un like à celui-ci.
Les données se mettront toujours automatiquement à jour sur vos appareils et en direct.
3.1 Ajouter des données depuis Flutter
Pour ajouter nos données depuis notre application Flutter, on créé un fichier séparé du nom de notre future page:
lib/add_movie_page.dart
C'est depuis cette page-là que nous allons créer notre formulaire d'envoi de données à Firebase.
Nous aurons aussi du package multiselect dont je vous laisse la documentation: https://pub.dev/packages/multiselect
C'est un package qui nous met à disposition le widget DropDownMultiSelect() pour permettre à l'utilisateur de choisir la ou les catégories du film.
Vous pouvez donc à ce moment importez les packages suivant dans votre nouveau fichier:
import 'package:flutter/material.dart'; import 'package:multiselect/multiselect.dart'; import 'package:cloud_firestore/cloud_firestore.dart';
Et déclarer notre AddPage sous forme de StatefulWidget et renvoyer le widget Scaffold():
class AddPage extends StatefulWidget { const AddPage({Key? key}) : super(key: key); @override State<AddPage> createState() => _AddPageState(); } class _AddPageState extends State<AddPage> { final nameController = TextEditingController(); final yearController = TextEditingController(); final posterController = TextEditingController(); List<String> categories = []; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Add Movie'), ), body: Padding( padding: const EdgeInsets.all(10), child: Column( children: [], ), ), ); } }
Vous pouvez alors importer ce nouveau fichier depuis main.dart et ajouter un bouton pour ouvrir cette nouvelle page dans la AppBar:
import 'add_movie_page.dart'; [...] class HomePage extends StatelessWidget { const HomePage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Flutter Firebase'), leading: IconButton( onPressed: () { Navigator.of(context).push( MaterialPageRoute( builder: (BuildContext context) { return const AddPage(); }, fullscreenDialog: true), ); }, icon: const Icon(Icons.add)), ), body: const SingleChildScrollView( child: MoviesInformation(), ), ); } }
On revient alors dans notre nouvelle page, dans laquelle nous allons créer un formulaire de contenu.
Tous les champs seront contenus dans une colonne, qui permettra de les afficher les uns après les autres.
On contiendra par exemple les champs texte dans une ListTile() qui contiendra un widget TextField().
Chaque TextField() devra être associé à un controller particulier, pour en récupérer la valeur par la suite.
Voilà par exemple le premier champ pour le nom du film associé au nameController:
ListTile( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(4), side: const BorderSide(color: Colors.white30, width: 1.5), ), title: Row( children: [ const Text('Nom: '), Expanded( child: TextField( decoration: const InputDecoration( border: InputBorder.none, ), controller: nameController, ), ), ], ), ),
Voilà celui pour l'année de production du film, associé au yearController:
ListTile( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(4), side: const BorderSide(color: Colors.white30, width: 1.5), ), title: Row( children: [ const Text('Année: '), Expanded( child: TextField( decoration: const InputDecoration( border: InputBorder.none, ), controller: yearController, ), ), ], ), ),
Et pour finir sur les champs textes, celui qui permet de récupérer l'adresse de l'affiche du film avec le posterController:
ListTile( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(4), side: const BorderSide(color: Colors.white30, width: 1.5), ), title: Row( children: [ const Text('Poster: '), Expanded( child: TextField( decoration: const InputDecoration( border: InputBorder.none, ), controller: posterController, ), ), ], ), ),
On passe maintenant au champ catégorie, pour sélectionner une ou plusieurs catégories pour notre film.
On utilise donc le nouveau widget DropDownMultiSelect() en proposant une liste de catégories:
DropDownMultiSelect( onChanged: (List<String> x) { setState(() { categories = x; }); }, options: const [ 'Action', 'Science-fition', 'Aventure', 'Comédie' ], selectedValues: categories, whenEmpty: 'Catégorie', ),
On reprend aussi la liste categories que nous avons déclarée plus haut, que récupère les choix de l'utilisateur.
Ici, on ne fonctionne pas avec un controller, et c'est donc cette liste categories que l'on utilisera pour envoyer les données dans Firebase.
Enfin, on termine par le bouton d'envoi de notre formulaire, avec un classique ElevatedButton():
ElevatedButton( style: ElevatedButton.styleFrom( minimumSize: const Size.fromHeight(50), ), onPressed: () { FirebaseFirestore.instance.collection('Movies').add({ 'name': nameController.value.text, 'year': yearController.value.text, 'poster': posterController.value.text, 'categories': categories, 'likes': 0, }); Navigator.pop(context); }, child: const Text('Ajouter'), ),
Ici, on associe le bouton avec l'envoi de nos informations dans la collection 'Movies', grâce au package cloud_firestore.
On crée une instance de notre package pour pouvoir utiliser ses fonctions, et on précise que l'on travaille dans la collection 'Movies'.
On utilise ensuite la méthode add() pou ajouter un nouveau document dans notre collection, avec toutes les informations de notre formulaire.
Pour récupérer la valeur d'un champ, on reprend le controller avec la syntaxe nameController.value.text.
Pour les catégories, on reprend directement la liste categories et pour les likes, on initialise à zéro.
Une fois que les données sont envoyées on ferme notre page avec la méthode pop().
3.2 Récupérer des données complexes
Vous pouvez donc tester l'envoi de donnée depuis votre application Flutter et regarder le résultat depuis Firebase.
Chaque nouveau film, et donc nouveau document devrait avoir le modèle suivant dans Firestore:
En reprenant le même MoviesInformation que tout à l'heure, nous serons capables d'afficher l'affiche du film avec le widget Image.network():
SizedBox( width: 100, child: Image.network(movie['poster']), ),
Le nom du film avec une police agrandie:
Text( movie['name'], style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold), ),
L'année de production de la même manière (tous ces widgets sont contenus dans une colonne):
const Text('Année de production'), Text(movie['year'].toString()),
Et enfin, pour les catégories, nous pouvons parcourir le champ 'categories' et renvoyer pour chaque élément le widget Chip():
Row( children: [ for (final categorie in movie['categories']) Padding( padding: const EdgeInsets.only(right: 5), child: Chip( backgroundColor: Colors.lightBlue, label: Text( categorie, style: const TextStyle(color: Colors.white), ), ), ), ], ),
Voilà pour les informations statiques de notre film, on termine avec les likes à modifier en temps réel.
3.3 Mettre à jour les données
Pour les likes, on les affiche à côté d'un IconButton() qui nous permettra d'ajouter un like autant de fois que l'on souhaite.
On utilise une Row() pour afficher le bouton et le texte côte à côte:
Row( children: [ IconButton( padding: EdgeInsets.zero, constraints: const BoxConstraints(), iconSize: 20, onPressed: () { addLike(document.id, movie['likes']); }, icon: const Icon(Icons.favorite), ), const SizedBox(width: 10), Text(movie['likes'].toString() + ' likes'), ], ),
Surtout, on associe le IconButton() à une fonction qui nous permet d'ajouter +1 à notre nombre de likes dans Firebase.
Pour cela on crée la fonction addLike() qui prend en paramètre l'identifiant de notre document ainsi que la dernière valeur des likes.
On ajoute ensuite +1 à cette valeur et on met à jour l'information dans Firebase:
void addLike(String docID, int likes) { var newLiks = likes + 1; try { FirebaseFirestore.instance.collection('Movies').doc(docID).update({ 'likes': newLiks, }).then((value) { print('Base Firestore à jour'); }); } catch (e) { print(e.toString()); } }
Grâce à la méthode update(), on peut modifier le champ d'un document Firestore en précisant son adresse précise (collection plus identifiant du document).
La magie de Firebase, c'est notre affichage se met automatiquement à jour à chaque nouveau like.
Voilà pour ce premier tuto Firebase accessible gratuitement sur la chaine YouTube et le blog.
Il s'agit d'une introduction à la formation Flutter Académie, qui propose un module entier dédié à Firebase.
Vous y apprendrez à créer un backend avancée pour vos applications Flutter, avec les Clouds Functions, le machine Learning ou encore les notifications push.