What is the difference between using DI using getIt vs Using Di from riverpod ?
#riverpod
Answer
Overview
Both GetIt and Riverpod provide Dependency Injection (DI) in Flutter, but with very different philosophies. GetIt is a pure service locator; Riverpod is a reactive state management + DI system.
GetIt — Service Locator Pattern
GetIt is a simple, global service locator with no Flutter dependency.
dart// pubspec.yaml // get_it: ^7.6.7 import 'package:get_it/get_it.dart'; final getIt = GetIt.instance; // Setup (usually in main.dart) void setupDependencies() { getIt.registerSingleton<ApiService>(ApiService()); getIt.registerSingleton<AuthRepository>(AuthRepository(getIt<ApiService>())); // Lazy — created on first access getIt.registerLazySingleton<UserRepository>( () => UserRepository(getIt<AuthService>()), ); // Factory — new instance each call getIt.registerFactory<OrderBloc>( () => OrderBloc(getIt<OrderRepository>()), ); } // Access anywhere (no context needed) final apiService = getIt<ApiService>(); final userRepo = getIt<UserRepository>();
Riverpod — Reactive DI
Riverpod provides DI through providers — reactive, compile-safe, and auto-disposing.
dart// Provider definitions (can be in any file) final apiServiceProvider = Provider((ref) => ApiService()); final authRepositoryProvider = Provider((ref) { return AuthRepository(ref.watch(apiServiceProvider)); // Compose }); final userRepositoryProvider = Provider((ref) { return UserRepository(ref.watch(authRepositoryProvider)); }); // Usage in widget or notifier class UserNotifier extends AsyncNotifier<User> { Future<User> build() async { final repo = ref.watch(userRepositoryProvider); // Get dependency return repo.getCurrentUser(); } }
Key Differences
| Feature | GetIt | Riverpod |
|---|---|---|
| Pattern | Service Locator (global registry) | Reactive providers |
| Context needed | ❌ No | ❌ No (ref needed) |
| Compile-time safety | ❌ No | ✅ Yes |
| Auto-dispose | ❌ Manual | ✅ text |
| Reactive | ❌ Not reactive | ✅ Fully reactive |
| State management | ❌ DI only | ✅ DI + State |
| Testing | ✅ Easy (override in setUp) | ✅ Easy (ProviderContainer) |
| Coupling | ⚠️ Global state | ✅ Scoped |
| Learning curve | ✅ Very simple | 🟡 Moderate |
Testing Comparison
dart// GetIt — reset in tests setUp(() { getIt.reset(); getIt.registerSingleton<ApiService>(MockApiService()); }); // Riverpod — override providers cleanly final container = ProviderContainer(overrides: [ apiServiceProvider.overrideWithValue(MockApiService()), ]); final userRepo = container.read(userRepositoryProvider);
Common Pattern — GetIt + BLoC
dart// GetIt is often paired with BLoC getIt.registerFactory<LoginBloc>( () => LoginBloc(getIt<AuthRepository>()), ); // In widget BlocProvider( create: (_) => getIt<LoginBloc>(), child: LoginScreen(), )
When to Use Which
| Use Case | Recommendation |
|---|---|
| Simple DI for services | GetIt (simple, no flutter dep) |
| DI + reactive state together | Riverpod |
| BLoC architecture | GetIt (common pairing) |
| Riverpod-first architecture | Riverpod providers |
| Need auto-dispose | Riverpod |
Trend: Many teams combine both — GetIt for DI of non-UI services (repos, APIs) and Riverpod for reactive UI state. Pure Riverpod architecture is increasingly preferred for new projects.