Injeção de Dependência em Flutter

Atualmente, estamos experimentando o Flutter enquanto desenvolvemos nosso projeto paralelo para enfrentar desafios com os colegas. Esse projeto paralelo também deve ser considerado um playground, onde podemos verificar se podemos usar o Flutter em projetos mais sérios. É por isso que queremos usar algumas abordagens que possam parecer um excesso de engenharia para um projeto tão pequeno.


Portanto, uma das primeiras perguntas foi o que podemos usar para injeção de dependência. Uma pesquisa rápida na internet revelou duas bibliotecas com críticas positivas: get_it e kiwi . Como o get_it acabou sendo um Localizador de Serviço (e eu não sou fã desse padrão), eu brincava com o kiwi, que parecia mais promissor, mas depois encontrei outra biblioteca: inject.dart . Ele é fortemente inspirado pela biblioteca Dagger e, como usamos o mais recente em nossos outros projetos para Android, decidi investigar.


Vale a pena dizer que, embora esta biblioteca esteja localizada no repositório do Google GitHub, não é uma biblioteca oficial do Google e atualmente não há suporte:


Atualmente, esta biblioteca é oferecida no estado em que se encontra (visualização do desenvolvedor), pois é de código aberto de um repositório interno dentro do Google. Como tal, não podemos agir sobre bugs ou solicitações de recursos no momento.

No entanto, parece que a biblioteca faz tudo o que precisamos por enquanto, então gostaria de compartilhar algumas informações sobre como você pode usar essa biblioteca em seu projeto.


Instalação


Como não há pacote no repositório oficial , temos que instalá-lo manualmente. Eu prefiro fazê-lo como um submódulo git, portanto, estou criando um vendor pastas no diretório de origem do projeto e executando o seguinte comando neste diretório:


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

E agora podemos configurá-lo adicionando as seguintes linhas ao 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


Que funcionalidade geralmente esperamos de uma biblioteca de DI? Vamos ver alguns casos de uso comuns:


Injeção na classe de concreto


Pode ser tão simples quanto isto:


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

Podemos usá-lo, por exemplo, com widgets Flutter como este:


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

Injeção de interface


Antes de tudo, precisamos definir uma classe abstrata com alguma classe de implementação, por exemplo:


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

E agora podemos fornecer dependências em nosso módulo:


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

Fornecedores


O que fazer se não precisarmos que uma instância de alguma classe seja injetada, mas um provedor, que nos dará uma nova instância dessa classe a cada vez? Ou se precisarmos resolver a dependência preguiçosamente em vez de obter uma instância concreta no construtor? Não encontrei nem na documentação (bem, porque não há documentação) nem nos exemplos fornecidos, mas funciona da mesma maneira que você pode solicitar uma função retornando a instância necessária e ela será injetada corretamente.


Podemos até definir um tipo de ajudante como este:


 typedef Provider<T> = T Function(); 

e use-o em nossas aulas:


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

Injeção assistida


Não há funcionalidade incorporada para injetar objetos que requerem argumentos conhecidos apenas em tempo de execução; portanto, podemos usar o padrão comum com fábricas neste caso: crie uma classe de fábrica que tome todas as dependências em tempo de compilação no construtor e injete-as e forneça um método factory com argumento de tempo de execução que criará uma instância necessária.


Singletons, qualificadores e injeção assíncrona


Sim, a biblioteca suporta tudo isso. Na verdade, há uma boa explicação no exemplo oficial .


Ligando-o


O passo final para fazer tudo funcionar é criar um injetor (também conhecido como componente do Dagger), por exemplo:


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

Aqui UserServices e DateResultsServices são módulos definidos anteriormente, MyApp é o widget raiz do nosso aplicativo e main.inject.dart é um arquivo gerado automaticamente (mais sobre isso mais adiante).


Agora podemos definir nossa função principal assim:


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

Running


Como o inject funciona com a geração de código, precisamos usar o build runner para gerar o código necessário. Nós podemos usar este comando:


 flutter packages pub run build_runner build 

ou watch para manter o código fonte sincronizado automaticamente:


 flutter packages pub run build_runner watch 

Mas há um momento importante aqui: por padrão, o código será gerado na pasta cache e o Flutter atualmente não suporta isso (embora haja um trabalho em andamento para resolver esse problema). Portanto, precisamos adicionar o arquivo inject_generator.build.yaml com o seguinte conteúdo:


 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 

Na verdade, é o mesmo conteúdo do arquivo vendor/inject.dart/package/inject_generator/build.yaml exceto por uma linha: build_to: cache foi substituída por build_to: source .


Agora podemos executar o build_runner , ele irá gerar o código necessário (e fornecer mensagens de erro se algumas dependências não puderem ser resolvidas) e, depois disso, executar o Flutter build como de costume.


Lucro


Só isso. Você também deve verificar os exemplos fornecidos com a própria biblioteca e, se tiver alguma experiência com a biblioteca Dagger, o inject será realmente muito familiar para você.

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


All Articles