Milad-Akarie / injectable

Code Generator for get_it
MIT License
565 stars 143 forks source link

Async singleton not working properly #423

Open warcayac opened 11 months ago

warcayac commented 11 months ago

I have this singleton:

final class OpenWeather {
  final String baseURL, apiKey, baseWeatherIconUrl;

  const OpenWeather._({required this.baseURL, required this.apiKey, required this.baseWeatherIconUrl});

  static OpenWeather? _instance;

  static Future<void> init() async {
    if (_instance != null) return;

    try {
      await dotenv.load(fileName: 'my.env');
      _instance = OpenWeather._(
        baseURL: dotenv.get('BASE_API_URL'),
        apiKey: dotenv.get('API_KEY'),
        baseWeatherIconUrl: dotenv.get('BASE_WEATHER_ICON_URL'),
      );
    } catch (e) {
      _instance = null;
      throw Exception('Env file cannot be loaded or some required field was not found.');
    }
  }

  static OpenWeather get instance {
    if (_instance != null) return _instance!;
    throw Exception('OpenWeather was not initialized.');
  }
}

I'm new with this package (and get_it too) so I'm getting a hard time about how to transform this class to use it with Injection package.

Another question is about how to notify other singletons to await to be created until an instance of OpenWeather exists (after init async method to be executed), example:

@singleton
class MyClass {
  void myMethod() {
    final value = OpenWeather.instance.baseURL;
    ...
     <code using this value>
    ...
  }
}

Try N° 1:

@singleton
final class OpenWeather {
  final String baseURL, apiKey, baseWeatherIconUrl;

  const OpenWeather({required this.baseURL, required this.apiKey, required this.baseWeatherIconUrl});

  @factoryMethod
  static Future<OpenWeather> init() async {
    try {
      await dotenv.load(fileName: 'my.env');
      return OpenWeather(
        baseURL: dotenv.get('BASE_API_URL'),
        apiKey: dotenv.get('API_KEY'),
        baseWeatherIconUrl: dotenv.get('BASE_WEATHER_ICON_URL'),
      );
    } catch (e) {
      throw Exception('Env file cannot be loaded or some required field was not found.');
    }
  }
}
@singleton
class MyClass {
  final OpenWeather openWeather;
  void myMethod(this.openWeather) {
    final value = openWeather.baseURL;
    ...
     <code using this value>
    ...
  }
}
import 'di.config.dart';

final getIt = GetIt.instance;  

@InjectableInit(  
  preferRelativeImports: true,
)
void configureDependencies() => getIt.init();
void main() async {
  configureDependencies();
  runApp(const AppProviders(child: MyApp()));
}

When running dart run build_runner build --delete-conflicting-outputs, it seems to be all OK so to check it I wanted to print a singleton value to my console from the home file of my app:

@RoutePage()
class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    final baseUrl = getIt<OpenWeather>().baseURL;
    print('baseUrl: $baseUrl');

    return Scaffold(
    ...

But I get this error message:

Launching lib/main.dart on Pixel 3a in debug mode...
✓  Built build/app/outputs/flutter-apk/app-debug.apk.
Connecting to VM Service at ws://127.0.0.1:46799/xrh7AgjvmbE=/ws

════════ Exception caught by widgets library ═══════════════════════════════════
The following StateError was thrown building HomeScreen(dirty):
Bad state: You tried to access an instance of OpenWeather that is not ready yet

The relevant error-causing widget was:
    HomeScreen HomeScreen:file:.../flutter_weather2/lib/config/routes/app_router.gr.dart:30:22

When the exception was thrown, this was the stack:
#0      throwIfNot (package:get_it/get_it_impl.dart:12:19)
#1      _GetItImplementation.get (package:get_it/get_it_impl.dart:434:7)
#2      _GetItImplementation.call (package:get_it/get_it_impl.dart:464:12)
#3      SearchScreen.build (package:flutter_weather2/modules/weather/3_presentation/2_screens/home/home_screen.dart:16:26)
#4      StatelessElement.build (package:flutter/src/widgets/framework.dart:5541:49)
#5      ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5471:15)
#6      Element.rebuild (package:flutter/src/widgets/framework.dart:5187:7)
#7      ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:5453:5)
#8      ComponentElement.mount (package:flutter/src/widgets/framework.dart:5447:5)
...     Normal element mounting (25 frames)
#33     Element.inflateWidget (package:flutter/src/widgets/framework.dart:4326:16)
#34     MultiChildRenderObjectElement.inflateWidget (package:flutter/src/widgets/framework.dart:6871:36)
#35     MultiChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:6883:32)
...     Normal element mounting (50 frames)
#85     Element.inflateWidget (package:flutter/src/widgets/framework.dart:4326:16)
#86     MultiChildRenderObjectElement.inflateWidget (package:flutter/src/widgets/framework.dart:6871:36)
#87     MultiChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:6883:32)
...     Normal element mounting (362 frames)
#449    Element.inflateWidget (package:flutter/src/widgets/framework.dart:4326:16)
#450    MultiChildRenderObjectElement.inflateWidget (package:flutter/src/widgets/framework.dart:6871:36)
#451    MultiChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:6883:32)
...     Normal element mounting (77 frames)
#528    Element.inflateWidget (package:flutter/src/widgets/framework.dart:4326:16)
#529    Element.updateChild (package:flutter/src/widgets/framework.dart:3831:20)
#530    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5496:16)
#531    StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5634:11)
#532    Element.rebuild (package:flutter/src/widgets/framework.dart:5187:7)
#533    StatefulElement.update (package:flutter/src/widgets/framework.dart:5657:5)
#534    Element.updateChild (package:flutter/src/widgets/framework.dart:3815:15)
#535    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5496:16)
#536    Element.rebuild (package:flutter/src/widgets/framework.dart:5187:7)
#537    ProxyElement.update (package:flutter/src/widgets/framework.dart:5800:5)
#538    Element.updateChild (package:flutter/src/widgets/framework.dart:3815:15)
#539    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5496:16)
#540    Element.rebuild (package:flutter/src/widgets/framework.dart:5187:7)
#541    ProxyElement.update (package:flutter/src/widgets/framework.dart:5800:5)
#542    Element.updateChild (package:flutter/src/widgets/framework.dart:3815:15)
#543    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5496:16)
#544    StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5634:11)
#545    Element.rebuild (package:flutter/src/widgets/framework.dart:5187:7)
#546    StatefulElement.update (package:flutter/src/widgets/framework.dart:5657:5)
#547    Element.updateChild (package:flutter/src/widgets/framework.dart:3815:15)
#548    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5496:16)
#549    Element.rebuild (package:flutter/src/widgets/framework.dart:5187:7)
#550    StatelessElement.update (package:flutter/src/widgets/framework.dart:5547:5)
#551    Element.updateChild (package:flutter/src/widgets/framework.dart:3815:15)
#552    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5496:16)
#553    Element.rebuild (package:flutter/src/widgets/framework.dart:5187:7)
#554    ProxyElement.update (package:flutter/src/widgets/framework.dart:5800:5)
#555    Element.updateChild (package:flutter/src/widgets/framework.dart:3815:15)
#556    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5496:16)
#557    Element.rebuild (package:flutter/src/widgets/framework.dart:5187:7)
#558    ProxyElement.update (package:flutter/src/widgets/framework.dart:5800:5)
#559    Element.updateChild (package:flutter/src/widgets/framework.dart:3815:15)
#560    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5496:16)
#561    StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5634:11)
#562    Element.rebuild (package:flutter/src/widgets/framework.dart:5187:7)
#563    BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2895:19)
#564    WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:984:21)
#565    RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:457:5)
#566    SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1325:15)
#567    SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1255:9)
#568    SchedulerBinding.scheduleWarmUpFrame.<anonymous closure> (package:flutter/src/scheduler/binding.dart:978:7)
#572    _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)
(elided 3 frames from class _Timer and dart:async-patch)
════════════════════════════════════════════════════════════════════════════════

So Where/which is the problem?


Try N° 2:

@Singleton()
final class OpenWeather {
  final String baseURL, apiKey, baseWeatherIconUrl;

  OpenWeather({required this.baseURL, required this.apiKey, required this.baseWeatherIconUrl});

  @FactoryMethod(preResolve: true)
  static Future<OpenWeather> init() async {
    try {
      await dotenv.load(fileName: 'assets/env/openweather.env');
      return OpenWeather(
        baseURL: dotenv.get('BASE_API_URL'),
        apiKey: dotenv.get('API_KEY'),
        baseWeatherIconUrl: dotenv.get('BASE_WEATHER_ICON_URL'),
      );
    } catch (e) {
      throw Exception('Env file cannot be loaded or some required field was not found.');
    }
  }
}
@InjectableInit(  
  preferRelativeImports: true,
)
Future<void> configureDependencies() async => GetIt.I.init();
void main() async {
  await configureDependencies();
  runApp(const AppProviders(child: MyApp()));
}

But I get this error message:

Launching .../flutter_weather2/lib/main.dart on Pixel 3a in debug mode...
✓  Built build/app/outputs/flutter-apk/app-debug.apk.
Connecting to VM Service at ws://127.0.0.1:41983/OxXw1kwrW-w=/ws

════════ Exception caught by widgets library ═══════════════════════════════════
The following StateError was thrown building SearchScreen(dirty):
Bad state: GetIt: Object/factory with type OpenWeather is not registered inside GetIt. 
(Did you accidentally do GetIt sl=GetIt.instance(); instead of GetIt sl=GetIt.instance;
Did you forget to register it?)

The relevant error-causing widget was:
    SearchScreen SearchScreen:file:.../flutter_weather2/lib/config/routes/app_router.gr.dart:30:22

When the exception was thrown, this was the stack:
#0      throwIfNot (package:get_it/get_it_impl.dart:12:19)
#1      _GetItImplementation._findFactoryByNameAndType (package:get_it/get_it_impl.dart:396:5)
#2      _GetItImplementation.get (package:get_it/get_it_impl.dart:424:29)
#3      _GetItImplementation.call (package:get_it/get_it_impl.dart:464:12)
#4      SearchScreen.build (package:flutter_weather2/modules/weather/3_presentation/2_screens/search/search_screen.dart:16:26)
#5      StatelessElement.build (package:flutter/src/widgets/framework.dart:5541:49)
#6      ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5471:15)
#7      Element.rebuild (package:flutter/src/widgets/framework.dart:5187:7)
#8      ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:5453:5)
#9      ComponentElement.mount (package:flutter/src/widgets/framework.dart:5447:5)
...     Normal element mounting (25 frames)
#34     Element.inflateWidget (package:flutter/src/widgets/framework.dart:4326:16)
#35     MultiChildRenderObjectElement.inflateWidget (package:flutter/src/widgets/framework.dart:6871:36)
#36     MultiChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:6883:32)
...     Normal element mounting (50 frames)
#86     Element.inflateWidget (package:flutter/src/widgets/framework.dart:4326:16)
#87     MultiChildRenderObjectElement.inflateWidget (package:flutter/src/widgets/framework.dart:6871:36)
#88     MultiChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:6883:32)
...     Normal element mounting (362 frames)
#450    Element.inflateWidget (package:flutter/src/widgets/framework.dart:4326:16)
#451    MultiChildRenderObjectElement.inflateWidget (package:flutter/src/widgets/framework.dart:6871:36)
#452    MultiChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:6883:32)
...     Normal element mounting (77 frames)
#529    Element.inflateWidget (package:flutter/src/widgets/framework.dart:4326:16)
#530    Element.updateChild (package:flutter/src/widgets/framework.dart:3831:20)
#531    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5496:16)
#532    StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5634:11)
#533    Element.rebuild (package:flutter/src/widgets/framework.dart:5187:7)
#534    StatefulElement.update (package:flutter/src/widgets/framework.dart:5657:5)
#535    Element.updateChild (package:flutter/src/widgets/framework.dart:3815:15)
#536    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5496:16)
#537    Element.rebuild (package:flutter/src/widgets/framework.dart:5187:7)
#538    ProxyElement.update (package:flutter/src/widgets/framework.dart:5800:5)
#539    Element.updateChild (package:flutter/src/widgets/framework.dart:3815:15)
#540    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5496:16)
#541    Element.rebuild (package:flutter/src/widgets/framework.dart:5187:7)
#542    ProxyElement.update (package:flutter/src/widgets/framework.dart:5800:5)
#543    Element.updateChild (package:flutter/src/widgets/framework.dart:3815:15)
#544    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5496:16)
#545    StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5634:11)
#546    Element.rebuild (package:flutter/src/widgets/framework.dart:5187:7)
#547    StatefulElement.update (package:flutter/src/widgets/framework.dart:5657:5)
#548    Element.updateChild (package:flutter/src/widgets/framework.dart:3815:15)
#549    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5496:16)
#550    Element.rebuild (package:flutter/src/widgets/framework.dart:5187:7)
#551    StatelessElement.update (package:flutter/src/widgets/framework.dart:5547:5)
#552    Element.updateChild (package:flutter/src/widgets/framework.dart:3815:15)
#553    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5496:16)
#554    Element.rebuild (package:flutter/src/widgets/framework.dart:5187:7)
#555    ProxyElement.update (package:flutter/src/widgets/framework.dart:5800:5)
#556    Element.updateChild (package:flutter/src/widgets/framework.dart:3815:15)
#557    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5496:16)
#558    Element.rebuild (package:flutter/src/widgets/framework.dart:5187:7)
#559    ProxyElement.update (package:flutter/src/widgets/framework.dart:5800:5)
#560    Element.updateChild (package:flutter/src/widgets/framework.dart:3815:15)
#561    ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5496:16)
#562    StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5634:11)
#563    Element.rebuild (package:flutter/src/widgets/framework.dart:5187:7)
#564    BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2895:19)
#565    WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:984:21)
#566    RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:457:5)
#567    SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1325:15)
#568    SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1255:9)
#569    SchedulerBinding.scheduleWarmUpFrame.<anonymous closure> (package:flutter/src/scheduler/binding.dart:978:7)
#573    _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)
(elided 3 frames from class _Timer and dart:async-patch)
════════════════════════════════════════════════════════════════════════════════
Application finished.

Exited.

Try N° 3:

@Singleton(signalsReady: true)
final class OpenWeather {
  final String baseURL, apiKey, baseWeatherIconUrl;
  /* -------------------------------------------------------------------------------------- */
  OpenWeather({required this.baseURL, required this.apiKey, required this.baseWeatherIconUrl}) {
    getIt.signalReady(this);
  }
  /* -------------------------------------------------------------------------------------- */
  @FactoryMethod()
  static Future<OpenWeather> init() async {
    try {
      await dotenv.load(fileName: 'assets/env/openweather.env');
      return OpenWeather(
        baseURL: dotenv.get('BASE_API_URL'),
        apiKey: dotenv.get('API_KEY'),
        baseWeatherIconUrl: dotenv.get('BASE_WEATHER_ICON_URL'),
      );
    } catch (e) {
      throw Exception('Env file cannot be loaded or some required field was not found.');
    }
  }
}
void main() async {
  configureDependencies();
  await getIt.allReady();
  runApp(const AppProviders(child: MyApp()));
}

But I get this error message:

Launching .../flutter_weather2/lib/main.dart on 192.168.56.101:5555 in debug mode...
✓  Built build/app/outputs/flutter-apk/app-debug.apk.
Connecting to VM Service at ws://127.0.0.1:37787/zwYCVaXC1xA=/ws
E/flutter ( 2324): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Exception: Env file cannot be loaded or some required field was not found.
E/flutter ( 2324): #0      OpenWeather.init (file:///.../flutter_weather2/lib/config/servers/open_weather_server.dart:25:7)
E/flutter ( 2324): <asynchronous suspension>
E/flutter ( 2324): #1      _GetItImplementation._register.<anonymous closure>.<anonymous closure> (package:get_it/get_it_impl.dart:1093:44)
E/flutter ( 2324): <asynchronous suspension>
E/flutter ( 2324): #2      FutureGroup.add.<anonymous closure> (package:async/src/future_group.dart:79:15)
E/flutter ( 2324): <asynchronous suspension>
E/flutter ( 2324):

Removing try..catch the error message is:

Launching .../flutter_weather2/lib/main.dart on 192.168.56.101:5555 in debug mode...
✓  Built build/app/outputs/flutter-apk/app-debug.apk.
Connecting to VM Service at ws://127.0.0.1:34433/Fep90TXZAEw=/ws
E/flutter ( 2266): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Bad state: This instance of the type OpenWeather is not available in GetIt If you have registered it as LazySingleton, are you sure you have used it at least once?
E/flutter ( 2266): #0      throwIf (package:get_it/get_it_impl.dart:7:18)
E/flutter ( 2266): #1      _GetItImplementation._findFactoryByInstance (package:get_it/get_it_impl.dart:1245:5)
E/flutter ( 2266): #2      _GetItImplementation.signalReady (package:get_it/get_it_impl.dart:1280:28)
E/flutter ( 2266): #3      new OpenWeather (file:.../flutter_weather2/lib/config/servers/open_weather_server.dart:12:11)
E/flutter ( 2266): #4      OpenWeather.init (file:.../flutter_weather2/lib/config/servers/open_weather_server.dart:19:14)
E/flutter ( 2266): <asynchronous suspension>
E/flutter ( 2266): #5      _GetItImplementation._register.<anonymous closure>.<anonymous closure> (package:get_it/get_it_impl.dart:1093:44)
E/flutter ( 2266): <asynchronous suspension>
E/flutter ( 2266): #6      FutureGroup.add.<anonymous closure> (package:async/src/future_group.dart:79:15)
E/flutter ( 2266): <asynchronous suspension>
E/flutter ( 2266):

Observations:

do the same thing but I don't know because the README file offers very basic help which does not allow proper use of the package, so which would be the correct solution to this problem?

github-actions[bot] commented 8 months ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions