Comment gérer l'état dans Flutter à l'aide du modèle BLoC

L'année dernière, j'ai choisi Flutter et je dois dire que ce fut un voyage formidable jusqu'à présent. Flutter est le formidable cadre de Google pour la création d'applications de haute qualité pour Android et iOS.

Comme pour la création de presque toutes les applications, il est toujours nécessaire de gérer l'état de l'application. Il est important que la gestion des états soit gérée efficacement, afin d'éviter d'accumuler des dettes techniques, d'autant plus que votre application se développe et devient plus complexe.

Dans Flutter, tous les composants de l'interface utilisateur sont des widgets. Lorsque vous commencez à composer ces widgets pour créer votre application géniale, vous vous retrouverez avec une arborescence de widgets profondément imbriqués. Ces widgets devront probablement partager l'état de l'application les uns avec les autres.

Dans cet article, nous verrons comment gérer l'état dans Flutter à l'aide du modèle BLoC.

La gestion de l'état dans Flutter peut être réalisée de différentes manières:

Widget hérité : il vous permet de propager des données vers ses widgets enfants et les widgets sont reconstruits chaque fois qu'il y a un changement dans l'état de l'application. L'inconvénient de l'utilisation de la classe de base InheritedWidget est que votre état est définitif et cela pose un problème si vous voulez muter votre état.

Modèle de portée : il s'agit d'un package externe construit sur InheritedWidget et il offre un moyen légèrement meilleur d'accéder, de mettre à jour et de muter l'état. Il vous permet de transmettre facilement un modèle de données d'un widget parent à ses descendants. En outre, il reconstruit également tous les enfants qui utilisent le modèle lorsque le modèle est mis à jour.

Cela peut poser un problème de performances, en fonction du nombre de ScopedModelDescendants d'un modèle, car ils sont reconstruits lors d'une mise à jour.

Ce problème peut être résolu en décomposant le ScopedModel en plusieurs modèles afin d'obtenir des dépendances plus fines. La définition de l' rebuildOnChangeindicateur falserésout également ce problème, mais cela entraîne la charge cognitive de décider quel widget doit être reconstruit ou non.

Redux : Oui! Comme avec React, il existe un package Redux qui vous aide à créer et à consommer facilement un magasin Redux dans Flutter. Comme son homologue JavaScript, il y a généralement quelques lignes de code standard et un aller-retour d' actions et de réducteurs .

Entrez le modèle BLoC

Le modèle BLoC (Business Logic Component) est un modèle créé par Google et annoncé lors de Google I / O '18. Le modèle BLoC utilise la programmation réactive pour gérer le flux de données dans une application.

Un BLoC se présente comme un intermédiaire entre une source de données dans votre application (par exemple une réponse API) et des widgets qui ont besoin de ces données. Il reçoit des flux d'événements / de données de la source, gère toute logique métier requise et publie des flux de modifications de données vers les widgets qui les intéressent.

Un BLoC a deux composants simples: les récepteurs et les flux , tous deux fournis par un StreamController . Vous ajoutez des flux d'événements / d'entrée de données dans un récepteur et les écoutez comme des flux de données sortant via un flux .

Un StreamController est accessible via la ‘dart:async’bibliothèque ou en tant que PublishSubject , ReplaySubject ou BehaviourSubject via le rxdartpackage.

Voici un extrait de code montrant un BLoC simple:

import 'dart:async'; // import 'package:rxdart/rxdart.dart'; if you want to make use of PublishSubject, ReplaySubject or BehaviourSubject. // make sure you have rxdart: as a dependency in your pubspec.yaml file to use the above import class CounterBloc { final counterController = StreamController(); // create a StreamController or // final counterController = PublishSubject() or any other rxdart option; Stream get getCount => counterController.stream; // create a getter for our Stream // the rxdart stream controllers returns an Observable instead of a Stream void updateCount() { counterController.sink.add(data); // add whatever data we want into the Sink } void dispose() { counterController.close(); // close our StreamController to avoid memory leak } } final bloc = CounterBloc(); // create an instance of the counter bloc //======= end of CounterBloc file //======= somewhere else in our app import 'counter_bloc.dart'; // import the counter bloc file here @override void dispose() { bloc.dispose(); // call the dispose method to close our StreamController super.dispose(); } ... @override Widget build(BuildContext context) { return StreamBuilder( // Wrap our widget with a StreamBuilder stream: bloc.getCount, // pass our Stream getter here initialData: 0, // provide an initial data builder: (context, snapshot) => Text('${snapshot.data}'), // access the data in our Stream here ); } ...

Un BLoC est une classe Dart simple. Dans l'extrait de code ci-dessus, nous avons créé une CounterBlocclasse et dans celle-ci, une StreamControllerque nous avons appelée counterController. Nous avons créé un getter pour notre Stream appelé getCount, une updateCountméthode qui ajoute des données dans notre Sink lorsqu'il est appelé, et une disposeméthode pour fermer notre StreamController.

Pour accéder aux données de notre Stream, nous avons créé un StreamBuilderwidget et passé notre Stream à sa streampropriété et avons accédé aux données dans sa builderfonction.

Mettre en œuvre BLoC

Nous allons convertir l'exemple d'application Flutter par défaut pour utiliser un BLoC. Allons-y et créons une nouvelle application Flutter. Dans votre terminal, exécutez la commande suivante:

$ flutter create bloc_counter && cd bloc_counter

Ouvrez l'application dans votre éditeur préféré et créer trois fichiers dans le dossier lib: counter.dart, counter_provider.dartet counter_bloc.dart.

Notre CounterProvidercontiendra un entier et une méthode pour l'incrémenter. Ajoutez le code suivant au counter_provider.dartfichier:

class CounterProvider { int count = 0; void increaseCount() => count++; }

Ensuite, nous implémenterons notre compteur BLoC. Ajoutez le code ci-dessous dans votre counter_block.dartfichier:

Dans notre CounterBlocclasse, nous avons utilisé une partie de notre exemple de code initial ci-dessus. À la ligne 7, nous avons instancié notre CounterProviderclasse et dans la updateCountméthode, nous avons appelé la méthode du fournisseur pour incrémenter le décompte, puis à la ligne 13, nous avons passé le décompte à notre Sink.

Replace the code in your main.dart file with the code below. In the code below, we simply removed most of the default counter code, which we’ll move to our counter.dart file. Whenever the incrementCounter method is called, we call the BLoC ‘s updateCount method which updates the count and adds it to our Sink.

Now, our BLoC is receiving and streaming data. We can access that data and display it on a screen through a StreamBuilder. We wrap whatever widget that needs the data in a StreamBuilder widget and pass the stream containing the data to it. Add the following code to the counter.dart file:

In the code above, we have a stateful widget. In our state class, on line 13, we call our bloc’s dispose method, so that stream controller can be closed whenever the widget is removed from the tree.

On line 19, we return a StreamBuilder widget and line 20, we pass the getter for our stream to it and also an initial data on line 21. The StreamBuilder also has a builder which gives us access to the data via a snapshot. On line 30, we access and display the data in the snapshot.

Go ahead and run the app by running the command below. Ensure you have an emulator running.

$ flutter run

With your app running, click the plus icon and watch the counter increase with every click.

Together, we’ve been able to implement the simplest form of a BLoC in Flutter. The concept remains the same regardless of your use case.

I hope you found this article useful. Please do and share so others can find this article. Hit me up on Twitter @developia_ with questions or for a chat.