What is dependency injection in Dart and how is it implemented?
TL;DR: DI provides dependencies from outside rather than creating them inside a class. GetIt is Flutter's go-to service locator. Flutter also supports constructor injection natively. DI enables testability (swap real with mock) and decoupling.
Full Answer
Without DI: UserService creates its own Dio() — impossible to test or swap. With DI: Dio is injected into UserService — swap with MockDio in tests.
Types of DI
- ▸Constructor injection — dependencies passed in constructor (preferred, most explicit)
- ▸Property injection — set after construction (less preferred, allows partially constructed objects)
- ▸Service locator (GetIt) — global registry; easy to use but hides dependencies (use sparingly)
GetIt Setup
GetIt is a simple service locator. Register singletons, factories, or lazy singletons. Access anywhere with GetIt.instance<T>(). Use injectable code generation to reduce boilerplate.
Code Examples
// Constructor injection: dependencies visible, easy to test // GetIt: convenient global access, watch for hidden coupling
Common Mistakes
- ✗Registering GetIt dependencies after they're first accessed — throws 'not registered' error at runtime
- ✗Using GetIt.instance everywhere instead of constructor injection — hides dependencies, harder to test
Interview Tip
Prefer constructor injection for business logic (visible dependencies, testable). Use GetIt at the composition root (main.dart/app startup) to wire dependencies together. This is the professional pattern.