Flutterando / modular

A smart project structure
https://pub.dev/packages/flutter_modular
Other
1.31k stars 254 forks source link

Injeçao de Dependencias #937

Open julioffigueiredo opened 8 months ago

julioffigueiredo commented 8 months ago

Tenho no meu AppModule

image

Esses 2 ultimos binds selecionados serão importados na tela de login puxados qdo importo o LoginController que está declarado no LoginModule

image

LoginController() receba a injeção de ApiAuthService() que recebe a injeção de ApiAuthRepository();

O problema é que ele parece que não olha os binds declarados no app_module

Exception has occurred. BindNotFoundException (BindNotFoundException: UnregisteredInstance: ApiAuthService not registered. Trace: LoginController->ApiAuthService LoginController => ApiAuthService

Estou fazendo algo de errado ou isso é bug, como estou usando a versão 6 pela primeira vez estou com esse problema, na versão 5 sempre foi certinho assim.

Caso eu passe o bind do LoginController() do login_module pro app_module, ele acha e faz a criação das classes certinho!

carlossulzer commented 8 months ago

Estou com o mesmo problema. Parece que os modulos não estão sendo importados. No meu caso usei o exportedBinds e também não funcionou. Estou utilizando a versão 6.3.2 do modular

julioffigueiredo commented 8 months ago

Estou com o mesmo problema. Parece que os modulos não estão sendo importados. No meu caso usei o exportedBinds e também não funcionou. Estou utilizando a versão 6.3.2 do modular

Carlos depois de procurar muito, e eu não sei se o que fiz está certo, mas mesmo assim postei aqui minha dúvida, pq do jeito q era antes, funcionava como uma árvore e era possível acessar tudo que fosse na linha acima. Mas coloquei esse código no modulo de login e ele "importou?!", mas achei bem esquisito.

@override List<Module> get imports => [ AppModule(), ];

FelCarv01 commented 8 months ago

Estou com o mesmo problema. Parece que os modulos não estão sendo importados. No meu caso usei o exportedBinds e também não funcionou. Estou utilizando a versão 6.3.2 do modular

Carlos depois de procurar muito, e eu não sei se o que fiz está certo, mas mesmo assim postei aqui minha dúvida, pq do jeito q era antes, funcionava como uma árvore e era possível acessar tudo que fosse na linha acima. Mas coloquei esse código no modulo de login e ele "importou?!", mas achei bem esquisito.

@override List<Module> get imports => [ AppModule(), ];

Julio está correto como você usou class XptoModule extends Module { @override void exportedBinds(i) { // Dependencies } } e onde quiseres usar as dependências class AbcModule extends Module { @override List<Module> get imports => [XptoModule()]; }

julioffigueiredo commented 8 months ago

Continuando a fazer testes aqui, eu acho que a função i.addSingleton()/Lazy está com algum tipo de bug, está sendo recriada. Seguindo a criação com os passos acima e disponibilizando um instancia de AppInfoStore, na AppModule via i.addSingleton(), e depois a importando na LoginModule via import, a cada criação eu printo um UUID que gero no construtor.

Não sei pq ele a cria 2 vezes e printa o seguinte: [log] ID criando o AppInfoStore: ac7a783f-fe40-490b-975d-84008f4b7c97 [log] ID criando o AppInfoStore: b2c465f3-bd50-49b5-8a2a-c1984ed8b9ea

Caso eu faça o Module.get() em units separadas ele me retorna [log] Id da instância do AppInfoStore na LoginController : b2c465f3-bd50-49b5-8a2a-c1984ed8b9ea [log] Id da instância do AppInfoStore na LoginPage ac7a783f-fe40-490b-975d-84008f4b7c97 Não sei como e pq ele pega uma instância diferente em cada unit

carlossulzer commented 8 months ago

No meu caso o problema estava no uso do SharedPreference com o modular. O flutter_modular não aceita bind assíncrono. Segue o código que funcionou para mim.

void main() async { WidgetsFlutterBinding.ensureInitialized(); SharedPreferences sharedPreferences = await SharedPreferences.getInstance();

runApp(ModularApp( module: AppModule(sharedPreferences: sharedPreferences), child: const AppWidget(), )); }

class AppModule extends Module { final SharedPreferences sharedPreferences;

AppModule({ required this.sharedPreferences, });

@override List get imports => [ CoreModule(sharedPreferences: sharedPreferences), AuthModule(), ];

@override void routes(RouteManager r) { r.module('/auth', module: AuthModule()); } }


class CoreModule extends Module { final SharedPreferences sharedPreferences; CoreModule({ required this.sharedPreferences, });

@override void exportedBinds(Injector i) { i.addLazySingleton((i) => LoginController());

i.addInstance<LocalStorage>(
  SharedPreferencesStorageImpl(instanceSharedPreference: sharedPreferences),
);
i.addSingleton<AuthStore>(AuthStore.new);

} }

jeffdgr8 commented 8 months ago

I experienced this same issue after upgrading from 5.0.3 to 6.3.2 and migrating to the new auto_injector API. At first I thought the same thing, that the AppModule dependencies weren't visible to the LoginModule, but after further testing, I discovered if I change the syntax from using the constructor function reference:

i.addLazySingleton(LoginController.new);

to calling the constructor explicitly in a lambda:

i.addLazySingleton(() => LoginController(i()));

then it works.

This seems to only be a problem when using a constructor function reference that requires a dependency from a different module. Using a constructor function reference seems to work as long as all its parameters are dependencies in the same module.

ktnishide commented 7 months ago

Modular 6.3.3 same here. when using .new , BindNotFoundException happens. if changed on child module to "lambda" it works

I experienced this same issue after upgrading from 5.0.3 to 6.3.2 and migrating to the new auto_injector API. At first I thought the same thing, that the AppModule dependencies weren't visible to the LoginModule, but after further testing, I discovered if I change the syntax from using the constructor function reference:

i.addLazySingleton(LoginController.new);

to calling the constructor explicitly in a lambda:

i.addLazySingleton(() => LoginController(i()));

then it works.

This seems to only be a problem when using a constructor function reference that requires a dependency from a different module. Using a constructor function reference seems to work as long as all its parameters are dependencies in the same module.

ktnishide commented 7 months ago

if I import all parent modules in the child module, it also works but doesn't seem to be aligned with what we see on Modular 5... However, other frameworks use this approach(importing all dependencies and not inheriting) such as Angular, Vue, etc... are you doing the same?

what are the guidelines? could you please document it since it is a breaking of paradigm from Modular 5 to 6.

importing parent modules:

class FeedModule extends Module {
  @override
  List<Module> get imports => [GlobalModule(), AppModule(), MenuModule()];

  @override
  void binds(i) {
    i.addSingleton<FeedStore>(FeedStore.new);
  }
...

using lambda syntax:

class FeedModule extends Module {
  @override
  void binds(i) {
    i.addSingleton<FeedStore>(() => FeedStore(i(), i(), i()));
  }
...
bielCostta commented 5 months ago

I had a similar problem. was reporting to me that my injections were not being found. After many tests it worked for me to change from:

i.add((i) => environment);

to:

i.add(() => environment);

I thought it was strange, but it worked. flutter_modular version: 6.3.4

jeffdgr8 commented 5 months ago

Version 6.3.4 seems to have now made this bug worse. Now even the

i.addLazySingleton(() => LoginController(i()));

syntax fails to find dependencies declared in a separate module.

The bug is actually in the modular_core version 3.3.3 dependency.

intellitour commented 4 months ago

Yep... same bug here. Anyone has find a workaround/fix for this? @jacobaraujo7 we need some help on this

eduardoflorence commented 4 months ago

Please, we need a minimum code to be placed here so that we can check the error (main + module + widgets). I can say that I work with version 6.3.4 and everything works normally

intellitour commented 4 months ago

I am available to book a call and demo the problem. Can't handle the code, though.

SamuelGadiel commented 3 months ago

Same problem here

julioffigueiredo commented 3 months ago

Segue um projeto demonstrando o problema.

Crio uma instância de cliente e endereço no app_module, o cliente recebe de injeção o endereço.

Normalmente eles são lidos dentro da tela Home.

Na Store, caso eu queira a classe Cliente via injeção ele me retorna o erro abaixo: [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: UnregisteredInstance: Cliente not registered. HomeStore => Cliente

Se eu puxar dentro da Store a classe via Modular.get(), a dependência é encontrada!

Então acho que há algum problema ao encontrar a dependência qdo ela vem por DI.

https://github.com/julioffigueiredo/modular_import.git

edugemini commented 3 months ago

Olá @julioffigueiredo,

Olhei seu exemplo e esse não é o modo correto de trabalhar com injeção de dependências compartilhadas no Modular 6.x.x. Se você precisa compartilhar dependências, elas precisam ser declaradas num módulo à parte, por exemplo CoreModule, em exportedBinds:

class CoreModule extends Module {
  @override
  void exportedBinds(Injector i) {
    i.addSingleton<Endereco>(Endereco.new);
    i.addSingleton<Cliente>(Cliente.new);
  }
}

Depois você pode importar em qualquer módulo que precise dessas dependências, como o seu AppModule e HomeModule (em ambos terá que fazer a importação). Exemplo em HomeModule:

class HomeModule extends Module {
  @override
  List<Module> get imports => [CoreModule()];

  // Aqui é só um exemplo para o caso de você precisar do bind que tem em CoreModule
  // para ser usado em outro bind da HomeModule. Neste exemplo, suponha que 
  // OutraClasse peça em seu construtor a instância de Cliente, então bastará fazer o código 
  // abaixo que ele conseguirá achar a instância de Cliente, pois foi importada de CoreModule
  @override
  void binds(i) {
    i.addSingleton(OutraClasse.new);
  }
}

Nas versões anteriores (<= 5.x.x) você precisava somente importar em AppModule e ele passava para os módulos abaixo, mas isso causava problema colaterais, então foi decidido por mudar a abordagem na versão 6.x.x

julioffigueiredo commented 3 months ago

@edugemini,

Eu conheço como usar o "core module", estou usando assim. Mas pq dentro do mesmo nível, por exemplo, no HomeStore, eu consigo importar via Modular.get() mas não consigo importar via DI? Se não pode/deve importar, não deveria importar por nenhum dos 2 modos. Pra mim isso é um bug um tanto quanto estranho! Já que eu mantenho o acesso sem ser via DI.