arch-atlas

Get it part 2

Presentation (Integration)

09:45

Summary

This lesson introduces the get_it package to implement the Service Locator pattern, addressing the "spaghetti code" caused by deeply nested dependencies in the UI layer. The instructor demonstrates how to register core services and repositories as Singletons, ensuring that objects like HomeRepoImpl and ApiService are only instantiated once and shared across the application. By centralizing this instantiation logic into a dedicated setup function, the code becomes significantly cleaner, avoids redundant memory allocations, and isolates dependency management from the widget tree.

Key Ideas

  • The get_it package acts as a Service Locator, allowing you to decouple interface from implementation and access registered objects anywhere in your app.
  • GetIt.instance creates a global accessor used to register and retrieve dependencies.
  • registerSingleton<T>() stores a single instance of a class, ensuring the exact same object in memory is returned on every subsequent request.
  • Using Singletons prevents the performance overhead of repeatedly re-instantiating heavy or shared objects, such as API services or data repositories.
  • getIt.get<T>() (or getIt<T>() via callables) retrieves the previously registered instance of the specified type.
  • Centralizing dependency instantiation into a setupServiceLocator() function declutters UI components like BlocProvider.

Code Snippets

Extracting dependency registration into a dedicated setup function (setup_service_locator.dart)dart
import 'package:dio/dio.dart';
import 'package:get_it/get_it.dart';
import '../api_service.dart';
import '../../features/home/data/data_sources/home_local_data_source.dart';
import '../../features/home/data/data_sources/home_remote_data_source.dart';
import '../../features/home/data/repos/home_repo_impl.dart';

final getIt = GetIt.instance;

void setupServiceLocator() {
  getIt.registerSingleton<ApiService>(
    ApiService(
      Dio(),
    ),
  );

  getIt.registerSingleton<HomeRepoImpl>(
    HomeRepoImpl(
      homeLocalDataSource: HomeLocalDataSourceImpl(),
      homeRemoteDataSource: HomeRemoteDataSourceImpl(
        getIt.get<ApiService>(),
      ),
    ),
  );
}
Retrieving the registered Singleton in the UI layer inside a BlocProviderdart
BlocProvider(
  create: (context) {
    return FeaturedBooksCubit(
      FetchFeaturedBooksUseCase(
        getIt.get<HomeRepoImpl>(),
      ),
    )..fetchFeaturedBooks();
  },
)

Architecture Insight

Architecture insight

Registering with getIt.registerLazySingleton is the right default for stateful infrastructure (data sources, repositories, API clients) — they're built on first request and live for the app's lifetime.

registerFactory is for things that should be fresh each call (most Cubits, transient holders). The line between them: ask "would two callers wanting their own copy be a bug or a feature?" Bug → singleton. Feature → factory.

My Notes

The instructor does a great job illustrating the "pain" of nested instantiation before introducing get_it, making the underlying "why" immediately clear. Extracting the registration logic into core/utils/functions/setup_service_locator.dart is a standard, scalable practice in Flutter.

  • [Editor's note] get_it is technically a Service Locator, not purely Dependency Injection (DI). While they solve the same problem (Inversion of Control), DI usually injects dependencies via constructors automatically, whereas a Service Locator requires the class to explicitly request dependencies.
  • [Editor's note] registerSingleton blocks synchronously. If you ever have dependencies that require await to initialize (like a local database or SharedPreferences), you should use getIt.registerSingletonAsync and ensure you await getIt.allReady() in your main() before calling runApp().
  • [Editor's note] Calling getIt.get<HomeRepoImpl>() inside the UI layer still slightly couples the UI to the Service Locator package. A stricter Clean Architecture approach would resolve this completely at the router or page builder level, but the instructor's approach is a widely accepted, pragmatic compromise in the Flutter community.