Flutter, Bloc and Firestore Stream, la combinaison parfaite, si utilisée de la bonne manière!

Arnaudelub
5 min readMar 4, 2021

--

This story is also available in english: Flutter, Bloc and Firestore Stream, the perfect match, if used the right way!

Esta story tambien esta disponible en español: Flutter, Bloc y Firestore Stream, ¡la combinación perfecta, si se usa de la manera correcta!

Ma toute première expérience Flutter

En 2018, le lendemain de mon embauche dans une entreprise en tant que développeur Angular, ils m'ont finalement dit que mon travail ne serait pas de développer avec Angular, mais de développer des applications en utilisant Flutter. J'étais comme, WTF! ce n'était pas notre deal et je n'avais jamais entendu parler de Flutter auparavant!. Mais alors j'ai pensé, hé, pourquoi pas, j’adore programmer et apprendre de nouvelles choses.

Alors ils m'ont donné 10 jours d'entraînement, par moi-même, en commençant avec le flutter cookbook (https://flutter.dev/docs/cookbook).

Après 2 jours de formation, honnêtement j'étais déjà fan de Flutter, un code pour Android et iOS, et desktop et web en beta (maintenant disponible, Flutter 2.0.0… YEAY !!!!), c'est tout simplement incroyable.

Donc à la fin de ma formation, prêt à commencer, ils m'ont demandé si AWS serait un bon choix pour le backend, et oui, c'est certainement le cas, mais je leur ai dit, "avez-vous également envisagé Firebase?" Je veux dire, c'est une plateforme Google, donc la compatibilité avec Flutter est probablement à 100%. Et alors devinez quoi, après cela, j’ai enfin commencé enfin à développer ma première application avec Flutter et Firebase. Heureux moi!

Le chemin du développement à la production avec ma première application n'a pas été facile, je me suis beaucoup battu avec de grosses erreurs qui ont presque coûté de l'argent à l'entreprise en factures de Google.

Je vais donc essayer d'en énumérer quelques-uns afin que cela ne vous arrivera pas.

C’est parti

Ok, tout d'abord, configurez votre projet Firebase (il y a beaucoup de documentation pour cela) et dans un premier temps, gardez le plan Spark, qui est le plan entièrement gratuit, aucune carte de crédit n'est demandée et vous avez presque toutes les fonctionnalités de Firebase disponibles. Mais si vous ou votre entreprise souhaitez passer au plan Blaze dès le début, la prochaine chose que vous devez faire est d'aller sur https://console.cloud.google.com/billing/, sélectionnez votre projet et sur le menu de gauche cliquez sur Budget et alerte, suivez les instructions et réglez le budget mensuel à 1€ et les alertes. Si vous ne le faites pas et si vous n'utilisez pas correctement StreamBuilder avec un Stream Firebase, vous risquez de passer une mauvaise journée. Pourquoi? vous x demandez vous, eh bien, ça vient:

Le StreamBuilder

Donc, vous créez cette belle application, et on vous a demandé de créer cet écran où le contenu doit être mis à jour automatiquement lorsque les données changent dans la base de données, et devinez quoi, les instantanés Firebase et StreamBuilders sont parfaits pour ce travail et si vous êtes nouveau sur Flutter, vous coderez probablement quelque chose comme ça dans un StatefullWidget:

StreamBuilder<DocumentSnapshot>(
stream: FirebaseFirestore.instance.collection('myCollection').doc(item.id).snapshots(),
builder: (context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if(snapshot.hasData) {
//Parsing your data, using setState(), etc...
} else {
//CircularProgressIndicator, handling error, etc...
}
});

Dans ces quelques lignes de code, il y a beaucoup de choses qui ne vont pas. Le premier appelle à chaque fois FirebaseFirestore.instance, FirebaseFirestore doit être instancié en tant que Singleton et pour cela, vous pouvez utiliser le plugin injectable (https://pub.dev/packages/injectable) et pour l’utiliser:

getIt<FirebaseFirestore>()

Ensuite, le DocumentSnapshot DOIT être déclaré dans votre initState si vous utilisez un StatefullWidget. Si vous ne le faites pas, chaque fois que votre widget est en cours de reconstruction, votre Stream sera de nouveau lancé jusqu'à ce que le widget soit supprimé. Imaginez que c'est un QuerySnapshot avec des centaines de documents, vous atteindrez la lecture gratuite de 50000 documents en un rien de temps et encore pire si vous utilisez setState dans le builder car cela provoquera une boucle infinie de reconstruction et ce n’est pas quelque chose que vous voulez.

Maintenant, si vous voulez vraiment le faire correctement, écrivez votre repository en utilisant injectable, utilisez le décorateur @lazySingleton, profitez de l'injection de dépendances pour injecter l'instance de FirebaseFirestore à votre classe, ce sera beaucoup plus propre. Voici un exemple de la façon dont je le fais normalement (en quelque sorte):

Normalement, l'erreur doit également être gérée dans le référentiel, j'utilise Either du package Dartz (https://pub.dev/packages/dartz) pour cela, mais c'est pour une autre story.

Utilisez des Stream avec Bloc

C'est donc l'une des premières choses que j'ai apprises de mon premier projet, mais l'autre gros problème que vous pourriez avoir est que vos widgets sont en rebuild partout et que c'est totalement hors de votre contrôle, donc votre application est lente, trop lourde. et la raison vient du fait que nous utilisons généralement des StatefullWidget partout au début. Le truc avec les StatefullWidget est que si l'état change, a.k.a lors de l'utilisation de setState (), toute la méthode build() est reconstruite. Donc, si vous voulez un contrôle plus fin sur la reconstruction, vous pouvez diviser autant que vous le pouvez votre widget avec d'autres widgets, ou vous pouvez utiliser Bloc avec le package flutter_bloc (https://pub.dev/packages/flutter_bloc) par exemple, et commencez à utiliser des StatelessWidgets.

Et avec cela, vient écouter un Stream en utilisant le modèle de bloc.

En fait, tout cela vient d'une question que j'ai trouvée sur Slack l'autre jour et je l'ai aidé à mettre en œuvre cela. Je pense que c'est une question légitime et la réponse ne vous vient pas directement à l'esprit.

Pour ce faire, vous aurez besoin de deux événements, un pour appeler le Stream de votre repository et l'écouter et un autre à appeler lorsque de nouvelles données sont reçues.

Pour garder le même exemple que précédemment, disons que nous voulons écouter cette méthode getLogs (), je devrai créer ces deux événements:

const factory LogEvent.watchLogsAsked() = WatchLogsAsked;
const factory LogEvent.logsReceived(List<Log> data) = LogsReceived;

maintenant, dans notre fichier bloc, nous pouvons implémenter la logique pour obtenir l'état que nous voulons, mais nous devons d'abord nous abonner aux événements du Stream et Stream.listen retourner un StreamSubscription afin que nous ayons:

StreamSubscription<List<Log>> _logsStreamSubscription;

Nous pouvons donc continuer et retourner notre State avec un yield:

yield* event.map(
watchLogsAsked: (WatchLogsAsked _) async* {
yield state.copyWith(logsAreLoading: true);
await _logStreamSubscription?.cancel();
_logsStreamSubscription = _repository.getLogs().listen((data) => add(LogEvent.logsReceived(data)));
},
logsReceived: (LogsReceived data) async* {
//Do your stuff here and yield your state
}
);

Et c'est fondamentalement tout, maintenant vous pouvez appeler BlocBuilder dans votre StatelessWidget et faire tout ce que vous voulez avec vos données, sachant que ce sera la seule partie à reconstruire lorsque de nouvelles données arriveront;

Et n'oubliez pas d'annuler votre Subscription également, remplacez simplement la méthode Bloc close() et annulez l'abonnement avec un cancel().

Voici un exemple de comment je l’implémente:

Vous pouvez voir que j'utilise Freezed pour la génération de code et Either pour la gestion des erreurs, si vous voulez en savoir plus à ce sujet, il y a beaucoup de tutoriels ici qui vous expliqueront comment cela fonctionne mieux que moi.

Eh bien, voila pour l’histoire, et c'est juste mon point de vue, nous savons tous qu'il y en a beaucoup et je ne prétends pas que cela soit la meilleure. Ce n'est qu'une possibilité et il y en a beaucoup d'autres, c'est l'une des meilleures choses que nous ayons, nous pouvons toujours apprendre de nouvelles choses, On ne s’ennuie jamais.

N'hésitez pas à me dire ce que vous pensez, si vous aimez ou n'aimez pas et pourquoi.

Happy coding @ tous!

--

--

Arnaudelub
Arnaudelub

Written by Arnaudelub

Flutter Developer enthousiast since 2018

No responses yet