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.