Dependency Injection Setup (GetIt) in Flutter App
GetIt is a service locator for Dart/Flutter, de facto standard for DI in projects that don't need code generation. Principle is simple: register dependencies once at app start, request anywhere via GetIt.instance<T>() or shorthand sl<T>().
Why GetIt Instead of Something Else
provider and Riverpod are State Management with DI as side effect. GetIt is pure service locator without Flutter dependencies: can use it in domain layer without BuildContext. For Clean Architecture where domain layer knows nothing about Flutter, this is principle.
GetIt constructor supports three registration modes:
-
registerSingleton<T>— creates immediately on registration, lives throughout app lifetime -
registerLazySingleton<T>— creates on first access, then returns same instance -
registerFactory<T>— creates new instance on every access
In practice: ApiService, DatabaseHelper, SharedPreferences wrapper — registerLazySingleton. ViewModel or BLoC if created by GetIt (rarely) — registerFactory. Usually BLoC created via BlocProvider, GetIt holds only infrastructure layer.
Common Initialization Mistake
// Wrong — synchronous registration of async dependency
sl.registerLazySingleton<DatabaseHelper>(() => DatabaseHelper()..init());
// Correct — async init via registerSingletonAsync
sl.registerSingletonAsync<DatabaseHelper>(() async {
final db = DatabaseHelper();
await db.init();
return db;
});
// And wait for readiness before runApp:
await sl.allReady();
Without registerSingletonAsync + allReady(), DatabaseHelper may be requested before async initialization completes — crash on startup with StateError: Singleton is not ready yet.
Organizing injection_container.dart
Standard practice — one file injection_container.dart (or di/) with initDependencies() function:
Future<void> initDependencies() async {
// External
final sharedPrefs = await SharedPreferences.getInstance();
sl.registerLazySingleton(() => sharedPrefs);
sl.registerLazySingleton(() => http.Client());
// Data sources
sl.registerLazySingleton<AuthRemoteDataSource>(
() => AuthRemoteDataSourceImpl(sl()),
);
// Repositories
sl.registerLazySingleton<AuthRepository>(
() => AuthRepositoryImpl(sl()),
);
// Use cases
sl.registerLazySingleton(() => LoginUseCase(sl()));
// BLoCs — if created via GetIt
sl.registerFactory(() => AuthBloc(loginUseCase: sl()));
}
Register in dependency order bottom-up: first external deps, then data sources, repositories, use cases, finally — presentation layer.
GetIt + flutter_modular or Feature-Based DI
On large projects single injection_container.dart becomes 500 lines. Solution: split by features. Each feature module registers own deps via separate function initAuthDependencies(), initProfileDependencies() — called from main initDependencies().
What's Included in Setup
Analyze current project architecture → identify layers and dependencies → configure injection_container with correct lifetimes → setup async initialization for DB and Storage → cover registration with unit tests via mock substitution in test setUp.
Work takes 1–2 days depending on project size and number of existing dependencies.







