Injection de dépendance dans Flutter

Nous expérimentons actuellement Flutter tout en développant notre projet parallèle pour les défis d'étape avec des collègues. Ce projet parallèle doit également être considéré comme un terrain de jeu, où nous pouvons vérifier si nous pouvons utiliser Flutter dans des projets plus sérieux. C'est pourquoi nous voulons utiliser certaines approches qui peuvent ressembler à une ingénierie excessive pour un si petit projet.


Donc, l'une des premières questions était ce que nous pouvons utiliser pour l'injection de dépendance. Une recherche rapide sur Internet a révélé 2 bibliothèques avec des critiques positives: get_it et kiwi . Comme get_it s'est avéré être un localisateur de services (et je ne suis pas fan de ce modèle), j'allais jouer avec le kiwi, qui semblait plus prometteur, mais j'ai ensuite trouvé une autre bibliothèque: inject.dart . Il est fortement inspiré de la bibliothèque Dagger, et comme nous utilisons le dernier de nos autres projets Android, j'ai décidé de creuser.


Il vaut la peine de dire que bien que cette bibliothèque soit située dans le référentiel Google GitHub, ce n'est pas une bibliothèque officielle de Google et aucun support n'est actuellement fourni:


Cette bibliothèque est actuellement proposée en l'état (aperçu développeur) car elle est open source à partir d'un référentiel interne à Google. En tant que tel, nous ne sommes pas en mesure d'agir sur les bogues ou les demandes de fonctionnalités pour le moment.

Néanmoins, il semble que la bibliothèque fasse tout ce dont nous avons besoin pour le moment, je voudrais donc partager quelques informations sur la façon dont vous pouvez utiliser cette bibliothèque dans votre projet.


L'installation


Comme il n'y a pas de package dans le référentiel officiel , nous devons l'installer manuellement. Je préfère le faire comme un sous-module git, donc je crée un vendor dossier dans mon répertoire source de projet et j'exécute la commande suivante à partir de ce répertoire:


 git submodule add https://github.com/google/inject.dart 

Et maintenant, nous pouvons le configurer en ajoutant les lignes suivantes dans pubspec.yaml :


 dependencies: // other dependencies here inject: path: ./vendor/inject.dart/package/inject dev_dependencies: // other dev_dependencies here build_runner: ^1.0.0 inject_generator: path: ./vendor/inject.dart/package/inject_generator 

Utilisation


Quelles fonctionnalités attendons-nous habituellement d'une bibliothèque DI? Passons en revue quelques cas d'utilisation courants:


Injection de classe de béton


Cela peut être aussi simple que cela:


 import 'package:inject/inject.dart'; @provide class StepService { // implementation } 

Nous pouvons l'utiliser par exemple avec des widgets Flutter comme ceci:


 @provide class SomeWidget extends StatelessWidget { final StepService _service; SomeWidget(this._service); } 

Injection d'interface


Tout d'abord, nous devons définir une classe abstraite avec une classe d'implémentation, par exemple:


 abstract class UserRepository { Future<List<User>> allUsers(); } class FirestoreUserRepository implements UserRepository { @override Future<List<User>> allUsers() { // implementation } } 

Et maintenant, nous pouvons fournir des dépendances dans notre module:


 import 'package:inject/inject.dart'; @module class UsersServices { @provide UserRepository userRepository() => FirestoreUserRepository(); } 

Fournisseurs


Que faire si nous n'avons pas besoin d'une instance d'une classe à injecter, mais plutôt d'un fournisseur, qui nous donnera à chaque fois une nouvelle instance de cette classe? Ou si nous devons résoudre la dépendance paresseusement au lieu d'obtenir une instance concrète dans le constructeur? Je ne l'ai trouvé ni dans la documentation (enfin, car il n'y a pas de documentation du tout) ni dans les exemples fournis, mais cela fonctionne en fait de cette façon que vous pouvez demander une fonction renvoyant l'instance requise et elle sera injectée correctement.


Nous pouvons même définir un type d'aide comme celui-ci:


 typedef Provider<T> = T Function(); 

et utilisez-le dans nos cours:


 @provide class SomeWidget extends StatelessWidget { final Provider<StepService> _service; SomeWidget(this._service); void _someFunction() { final service = _service(); // use service } } 

Injection assistée


Il n'y a pas de fonctionnalité intégrée pour injecter des objets qui nécessitent uniquement des arguments connus au moment de l'exécution, nous pouvons donc utiliser le modèle commun avec les usines dans ce cas: créer une classe d'usine qui prend toutes les dépendances de compilation dans le constructeur et l'injecter, et fournir un méthode d'usine avec argument d'exécution qui créera une instance requise.


Singletons, qualificatifs et injection asynchrone


Oui, la bibliothèque prend en charge tout cela. Il y a en fait une bonne explication dans l' exemple officiel .


Câblage


La dernière étape pour que tout fonctionne est de créer un injecteur (aka composant de Dagger), par exemple comme ceci:


 import 'main.inject.dart' as g; @Injector(const [UsersServices, DateResultsServices]) abstract class Main { @provide MyApp get app; static Future<Main> create( UsersServices usersModule, DateResultsServices dateResultsModule, ) async { return await g.Main$Injector.create( usersModule, dateResultsModule, ); } } 

Ici, UserServices et DateResultsServices sont des modules définis précédemment, MyApp est le widget racine de notre application, et main.inject.dart est un fichier généré automatiquement (plus de détails plus loin).


Maintenant, nous pouvons définir notre fonction principale comme ceci:


 void main() async { var container = await Main.create( UsersServices(), DateResultsServices(), ); runApp(container.app); } 

Courir


Comme inject fonctionne avec la génération de code, nous devons utiliser build runner pour générer le code requis. Nous pouvons utiliser cette commande:


 flutter packages pub run build_runner build 

ou watch afin de garder le code source synchronisé automatiquement:


 flutter packages pub run build_runner watch 

Mais il y a un moment important ici: par défaut, le code sera généré dans le dossier cache et Flutter ne le supporte pas actuellement (bien qu'il y ait un travail en cours pour résoudre ce problème). Nous devons donc ajouter le fichier inject_generator.build.yaml avec le contenu suivant:


 builders: inject_generator: target: ":inject_generator" import: "package:inject_generator/inject_generator.dart" builder_factories: - "summarizeBuilder" - "generateBuilder" build_extensions: ".dart": - ".inject.summary" - ".inject.dart" auto_apply: dependents build_to: source 

Il s'agit en fait du même contenu que dans le fichier vendor/inject.dart/package/inject_generator/build.yaml sauf pour une ligne: build_to: cache a été remplacé par build_to: source .


Maintenant, nous pouvons exécuter le build_runner , il générera le code requis (et fournira des messages d'erreur si certaines dépendances ne peuvent pas être résolues) et après cela, nous pouvons exécuter le build Flutter comme d'habitude.


Bénéfice


C'est tout. Vous devriez également vérifier les exemples fournis avec la bibliothèque elle-même, et si vous avez une certaine expérience avec la bibliothèque Dagger, l' inject sera très familière.

Source: https://habr.com/ru/post/fr446134/


All Articles