Inyección de dependencia en aleteo

Actualmente estamos experimentando con Flutter mientras desarrollamos nuestro proyecto paralelo para desafíos paso a paso con colegas. Este proyecto paralelo también debe considerarse como un patio de recreo, donde podemos verificar si podemos usar Flutter en proyectos más serios. Es por eso que queremos usar algunos enfoques que puedan parecer una ingeniería excesiva para un proyecto tan pequeño.


Entonces, una de las primeras preguntas fue qué podemos usar para la inyección de dependencia. Una búsqueda rápida en Internet reveló 2 bibliotecas con críticas positivas: get_it y kiwi . Como get_it resultó ser un Localizador de servicios (y no soy un fanático de este patrón), iba a jugar con kiwi, que parecía más prometedor, pero luego encontré otra biblioteca: inject.dart . Está inspirado en gran medida por la biblioteca Dagger, y como utilizamos el último en nuestros otros proyectos de Android, he decidido profundizar en él.


Vale la pena decir que aunque esta biblioteca se encuentra en el repositorio de Google GitHub, no es una biblioteca oficial de Google y actualmente no se proporciona soporte:


Esta biblioteca se ofrece actualmente tal cual (vista previa del desarrollador) ya que es de código abierto desde un repositorio interno dentro de Google. Como tal, no podemos actuar sobre errores o solicitudes de funciones en este momento.

Sin embargo, parece que la biblioteca hace todo lo que necesitamos por ahora, por lo que me gustaría compartir información sobre cómo puede usar esta biblioteca en su proyecto.


Instalación


Como no hay un paquete en el repositorio oficial , tenemos que instalarlo manualmente. Prefiero hacerlo como un submódulo git, por lo que estoy creando un vendor carpetas en el directorio de origen de mi proyecto y ejecuto el siguiente comando desde este directorio:


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

Y ahora podemos configurarlo agregando las siguientes líneas en 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 

Uso


¿Qué funcionalidad usualmente esperamos de una biblioteca DI? Veamos algunos casos de uso comunes:


Inyección de hormigón


Puede ser tan simple como esto:


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

Podemos usarlo, por ejemplo, con widgets Flutter como este:


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

Inyección de interfaz


En primer lugar, necesitamos definir una clase abstracta con alguna clase de implementación, por ejemplo:


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

Y ahora podemos proporcionar dependencias en nuestro módulo:


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

Proveedores


¿Qué hacer si no necesitamos inyectar una instancia de alguna clase, sino un proveedor, que nos dará una nueva instancia de esta clase cada vez? ¿O si necesitamos resolver la dependencia perezosamente en lugar de obtener una instancia concreta en el constructor? No lo encontré ni en la documentación (bueno, porque no hay documentación en absoluto) ni en los ejemplos proporcionados, pero en realidad funciona de esta manera que puede solicitar una función que devuelva la instancia requerida y se inyectará correctamente.


Incluso podemos definir un tipo de ayuda como este:


 typedef Provider<T> = T Function(); 

y úsalo en nuestras clases:


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

Inyección asistida


No hay una funcionalidad integrada para inyectar objetos que requieren argumentos conocidos solo en tiempo de ejecución, por lo que podemos usar el patrón común con fábricas en este caso: crear una clase de fábrica que tome todas las dependencias en tiempo de compilación en el constructor e inyectarla, y proporcionar un Método de fábrica con argumento de tiempo de ejecución que creará una instancia requerida.


Singletons, calificadores e inyección asincrónica


Sí, la biblioteca admite todo esto. En realidad, hay una buena explicación en el ejemplo oficial .


Cableado


El último paso para que todo funcione es crear un inyector (también conocido como componente de Dagger), por ejemplo, así:


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

Aquí UserServices y DateResultsServices son módulos previamente definidos, MyApp es el widget raíz de nuestra aplicación, y main.inject.dart es un archivo generado automáticamente (más sobre esto más adelante).


Ahora podemos definir nuestra función principal de esta manera:


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

Corriendo


Como inject funciona con la generación de código, necesitamos usar el runner de compilación para generar el código requerido. Podemos usar este comando:


 flutter packages pub run build_runner build 

o watch comando para mantener el código fuente sincronizado automáticamente:


 flutter packages pub run build_runner watch 

Pero hay un momento importante aquí: de forma predeterminada, el código se generará en la carpeta de cache y Flutter actualmente no lo admite (aunque hay un trabajo en progreso para resolver este problema). Por lo tanto, debemos agregar el archivo inject_generator.build.yaml con el siguiente contenido:


 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 

En realidad, es el mismo contenido que en el archivo vendor/inject.dart/package/inject_generator/build.yaml excepto por una línea: build_to: cache ha sido reemplazado por build_to: source .


Ahora podemos ejecutar build_runner , generará el código requerido (y proporcionará mensajes de error si no se pueden resolver algunas dependencias) y luego podremos ejecutar Flutter build como de costumbre.


Ganancia


Eso es todo También debe consultar los ejemplos proporcionados con la biblioteca en sí, y si tiene alguna experiencia con la biblioteca Dagger, inject será realmente muy familiar para usted.

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


All Articles