Abhängigkeitsinjektion beim Flattern

Wir experimentieren derzeit mit Flutter, während wir unser Nebenprojekt für schrittweise Herausforderungen mit Kollegen entwickeln. Dieses Nebenprojekt sollte auch als Spielplatz betrachtet werden, auf dem wir prüfen können, ob wir Flutter in ernsthafteren Projekten einsetzen können. Aus diesem Grund möchten wir dort einige Ansätze verwenden, die für ein so kleines Projekt wie eine Überentwicklung aussehen können.


Eine der ersten Fragen war also, was wir für die Abhängigkeitsinjektion verwenden können. Eine schnelle Suche im Internet ergab 2 Bibliotheken mit positiven Bewertungen: get_it und kiwi . Als sich herausstellte, dass get_it ein Service Locator ist (und ich bin kein Fan dieses Musters), wollte ich mit Kiwi spielen, was vielversprechender aussah, aber dann habe ich eine andere Bibliothek gefunden: injizieren.dart . Es ist stark von der Dolchbibliothek inspiriert, und da wir die neueste in unseren anderen Android-Projekten verwenden, habe ich mich entschlossen, mich damit zu beschäftigen.


Es ist erwähnenswert, dass sich diese Bibliothek zwar im Google GitHub-Repository befindet, jedoch keine offizielle Bibliothek von Google ist und derzeit keine Unterstützung bereitgestellt wird:


Diese Bibliothek wird derzeit unverändert angeboten (Entwicklervorschau), da sie aus einem internen Repository in Google als Open-Source-Version bereitgestellt wird. Daher können wir derzeit nicht auf Fehler oder Funktionsanforderungen reagieren.

Trotzdem sieht es so aus, als ob die Bibliothek alles tut, was wir jetzt brauchen. Deshalb möchte ich einige Informationen darüber teilen, wie Sie diese Bibliothek in Ihrem Projekt verwenden können.


Installation


Da sich kein Paket im offiziellen Repository befindet , müssen wir es manuell installieren. Ich bevorzuge es als Git-Submodul, daher erstelle ich einen vendor in meinem Projektquellverzeichnis und führe den folgenden Befehl aus diesem Verzeichnis aus:


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

Und jetzt können wir es einrichten, indem wir die folgenden Zeilen in 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 

Verwendung


Welche Funktionalität erwarten wir normalerweise von einer DI-Bibliothek? Lassen Sie uns einige gängige Anwendungsfälle durchgehen:


Betonklasseneinspritzung


So einfach kann das sein:


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

Wir können es zB mit Flutter-Widgets wie diesen verwenden:


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

Schnittstelleninjektion


Zunächst müssen wir eine abstrakte Klasse mit einer Implementierungsklasse definieren, z.


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

Und jetzt können wir Abhängigkeiten in unserem Modul bereitstellen:


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

Anbieter


Was tun, wenn keine Instanz einer Klasse injiziert werden muss, sondern ein Anbieter, der uns jedes Mal eine neue Instanz dieser Klasse gibt? Oder wenn wir die Abhängigkeit träge auflösen müssen, anstatt eine konkrete Instanz im Konstruktor zu erhalten? Ich habe es weder in der Dokumentation (naja, weil es überhaupt keine Dokumentation gibt) noch in den bereitgestellten Beispielen gefunden, aber es funktioniert tatsächlich so, dass Sie eine Funktion anfordern können, die die erforderliche Instanz zurückgibt, und sie wird ordnungsgemäß injiziert.


Wir können sogar einen Hilfstyp wie diesen definieren:


 typedef Provider<T> = T Function(); 

und benutze es in unseren Klassen:


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

Assistierte Injektion


Es gibt keine integrierte Funktionalität zum Einfügen von Objekten, für die Argumente erforderlich sind, die nur zur Laufzeit bekannt sind. Daher können wir in diesem Fall das allgemeine Muster für Fabriken verwenden: Erstellen Sie eine Factory-Klasse, die alle Abhängigkeiten zur Kompilierungszeit im Konstruktor berücksichtigt und einfügt, und geben Sie a an Factory-Methode mit Laufzeitargument, mit der eine erforderliche Instanz erstellt wird.


Singletons, Qualifikanten und asynchrone Injektion


Ja, die Bibliothek unterstützt all dies. Das offizielle Beispiel enthält tatsächlich eine gute Erklärung.


Verkabelung


Der letzte Schritt, damit alles funktioniert, besteht darin, einen Injektor (auch bekannt als Komponente von Dagger) zu erstellen, z. B.:


 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, ); } } 

Hier sind UserServices und DateResultsServices zuvor definierte Module, MyApp ist das Root-Widget unserer Anwendung und main.inject.dart ist eine automatisch generierte Datei (dazu später mehr).


Jetzt können wir unsere Hauptfunktion folgendermaßen definieren:


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

Laufen


Da inject mit der Codegenerierung funktioniert, müssen wir Build Runner verwenden, um den erforderlichen Code zu generieren. Wir können diesen Befehl verwenden:


 flutter packages pub run build_runner build 

oder Befehl watch , um den Quellcode automatisch zu synchronisieren:


 flutter packages pub run build_runner watch 

Hier gibt es jedoch einen wichtigen Moment: Standardmäßig wird der Code im cache Ordner generiert, und Flutter unterstützt dies derzeit nicht (obwohl derzeit daran gearbeitet wird, dieses Problem zu lösen). Daher müssen wir die Datei inject_generator.build.yaml mit folgendem Inhalt hinzufügen:


 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 

Es ist tatsächlich der gleiche Inhalt wie in der Datei vendor/inject.dart/package/inject_generator/build.yaml Ausnahme einer Zeile: build_to: cache wurde durch build_to: source .


Jetzt können wir den build_runner , er generiert den erforderlichen Code (und gibt Fehlermeldungen aus, wenn einige Abhängigkeiten nicht gelöst werden können) und danach können wir Flutter build wie gewohnt ausführen.


Gewinn


Das ist alles Sie sollten auch die Beispiele überprüfen, die mit der Bibliothek selbst geliefert wurden. Wenn Sie Erfahrung mit der Dolchbibliothek haben, ist inject Ihnen wirklich sehr vertraut.

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


All Articles