flutterdartdependency-injectionservice-locator

What does serviceLocator() mean in GetIt?


I am trying to master GetIt. First I read the official documentation. Then I came to articles by different authors. Finally, I took a working code to get some practice.

And I don't understand the following. I have code that works great:

final serviceLocator = GetIt.instance;

setupServiceLocator() async {
  serviceLocator.registerFactory<Constant>(() => Constant());
  serviceLocator.registerFactory<Dio>(
    () => NetworkClient(Dio(), constant: serviceLocator()).dio,
  );

  // News
  serviceLocator.registerFactory<MainNewsBloc>(() => MainNewsBloc());
  serviceLocator.registerLazySingleton<NewsApi>(() => NewsApi(
      dio: serviceLocator(), apiKey: serviceLocator<Constant>().apiKey));
  serviceLocator.registerLazySingleton<NewsRepository>(() => NewsRepositoryImpl(
      newsApi: serviceLocator(), apiKey: serviceLocator<Constant>().apiKey));
  serviceLocator.registerLazySingleton<GetRecentNewsUseCase>(
      () => GetRecentNewsUseCase(serviceLocator()));
}

I understand lines like:

serviceLocator.registerFactory<Constant>(() => Constant());
...
serviceLocator.registerFactory<MainNewsBloc>(() => MainNewsBloc());

But I don't understand lines like:

serviceLocator.registerLazySingleton<NewsApi>(() => NewsApi(
      dio: serviceLocator(), apiKey: serviceLocator<Constant>().apiKey));

In particular, I don't understand the following:

 dio: serviceLocator(),
...
 newsApi: serviceLocator(),
...
serviceLocator.registerLazySingleton<GetRecentNewsUseCase>(
      () => GetRecentNewsUseCase(serviceLocator()));

Why is serviceLocator() used in all these cases? What does this mean?

Of course, I should ask this question to the author of this code. But I believe that there are enough experienced programmers here to explain to me what serviceLocator() means as arguments in all these cases.


Solution

  • The short answer it's the combination of the callable class and type-inferance.

    1. callable classes: By serviceLocator() in this kind of syntax, you are invoking a method with the name call inside the object.

    To understand this you can refer to this code example:

    void main(List<String> args) {
      const callable = CallableClass();
      callable();
    }
    
    class CallableClass {
      const CallableClass();
    
      void call() {
        print('invoked call method inside callable class');
      }
    }
    
    1. type-inferance: by specifying the return type T of a generic function you are passing the type parameter as T to that function type param.

    The internal implementation of get_It looks like this:

      @override
      T call<T extends Object>({
        String? instanceName,
        dynamic param1,
        dynamic param2,
      }) {
        return get<T>(instanceName: instanceName, param1: param1, param2: param2);
      }
    
    

    for understanding consider the following dart snipper:

    void main(List<String> args) {
      final int value = findWhereTypeIsT();
      print(value); // 10
    }
    
    T findWhereTypeIsT<T>() {
      final objects = <Object>[10, "something"];
      return objects.whereType<T>().first;
    }
    

    And according to get it docs: Callable class so that you can write GetIt.instance<MyType> instead of GetIt.instance.get<MyType>