Managing dependencies in a Flutter app becomes crucial as the project grows. GetIt is a simple service locator that can be used alongside Bloc to provide dependencies like repositories, use cases, and blocs themselves. This guide shows you how to integrate GetIt with Bloc, register dependencies, and write clean, testable code.
What is GetIt?
GetIt is a service locator (not a dependency injection container) that gives you a global object to access your dependencies. It allows you to register singletons, factories, and lazy instances, and then retrieve them anywhere in your app. When combined with Bloc, it helps decouple your UI from the creation of blocs and their dependencies.
Installation
Run flutter pub get to install.
Setting Up GetIt
Create a file (e.g., lib/di/injection.dart) to register all your dependencies. You'll typically create a GetIt instance and register dependencies in a function called init().
Initializing GetIt in main
Using GetIt in Blocs
Now you can create blocs that receive their dependencies via constructor. When you need a bloc, you can retrieve it from GetIt.
To use this bloc in a widget, you can get it from GetIt and provide it with BlocProvider.
Registering Different Types of Dependencies
| Method | Use Case | Example |
|---|---|---|
| `registerSingleton` | Single instance for the whole app | `getIt.registerSingleton(MyService())` |
| `registerLazySingleton` | Created on first access | `getIt.registerLazySingleton(() => MyService())` |
| `registerFactory` | New instance each time | `getIt.registerFactory(() => MyBloc(getIt()))` |
| `registerSingletonAsync` | Async initialization | `getIt.registerSingletonAsync(() async => await createDb())` |
Injecting Dependencies into Blocs
With GetIt, you can inject repositories and use cases directly into blocs. This keeps blocs focused and testable. For example:
Testing with GetIt
GetIt makes testing easy because you can replace dependencies with mocks. In your test files, you can reset GetIt and register mock implementations before creating the bloc under test.
GetIt vs BlocProvider
BlocProvider is still used to provide blocs to the widget tree so that they can be accessed via context.read and automatically disposed. GetIt is used for creating the bloc instances and injecting their dependencies. The two work together: GetIt creates the bloc, and BlocProvider passes it to the widget tree.
Best Practices
- Use
registerFactoryfor blocs – Blocs hold state and are usually scoped to a screen; a new instance is needed each time the screen is created. - Use
registerLazySingletonfor services/repositories – These are stateless and can be reused across the app. - Initialize GetIt in
mainbeforerunApp– Ensures all dependencies are ready before the UI builds. - Avoid using
GetItdirectly in UI code – Instead, retrieve the bloc viaBlocProviderand usecontext.read. Keep GetIt for creation only. - Reset GetIt between tests – Use
getIt.reset()insetUpto avoid test pollution. - Use
getIt<MyType>()with a type hint – Explicit types help readability.
Common Mistakes
- ❌ Registering blocs as singletons – Can cause state to persist across screens, leading to bugs.
✅ Use
registerFactoryfor blocs. - ❌ Calling
getIt<Bloc>()inside build methods – This may create a new instance on each rebuild if the registration is a factory. ✅ Provide the bloc viaBlocProviderand usecontext.read. - ❌ Not resetting GetIt in tests – Causes dependencies to leak between tests.
✅ Call
getIt.reset()insetUp. - ❌ Registering async dependencies without awaiting
allReady– The app may start before dependencies are ready. ✅ UsegetIt.allReady()after registering async dependencies.
Conclusion
GetIt is a powerful complement to Bloc for managing dependencies in Flutter apps. It centralizes dependency creation, simplifies testing, and keeps your blocs clean and focused. By combining GetIt with BlocProvider, you get the best of both worlds: a service locator for creation and InheritedWidget for scoped access. Adopt this pattern to build scalable, maintainable Flutter applications.