Question #278EasyState ManagementImportant

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

FeatureGetItRiverpod
PatternService Locator (global registry)Reactive providers
Context needed❌ No❌ No (ref needed)
Compile-time safety❌ No✅ Yes
Auto-dispose❌ Manual
text
.autoDispose
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 CaseRecommendation
Simple DI for servicesGetIt (simple, no flutter dep)
DI + reactive state togetherRiverpod
BLoC architectureGetIt (common pairing)
Riverpod-first architectureRiverpod providers
Need auto-disposeRiverpod

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.