Answer
Overview
Injectable is a code generation package for Dependency Injection (DI) in Flutter, built on top of
text
get_itInstallation
Add to
text
pubspec.yamlyamldependencies: injectable: ^2.3.0 get_it: ^7.6.0 dev_dependencies: injectable_generator: ^2.4.0 build_runner: ^2.4.0
Setup
1. Create Injection Configuration
lib/injection.dart:
dartimport 'package:get_it/get_it.dart'; import 'package:injectable/injectable.dart'; import 'injection.config.dart'; // Generated file final getIt = GetIt.instance; () void configureDependencies() => getIt.init();
2. Initialize in main.dart
dartimport 'injection.dart'; void main() { configureDependencies(); // Register all dependencies runApp(MyApp()); }
3. Generate Code
bashflutter pub run build_runner build --delete-conflicting-outputs
This generates
text
injection.config.dartCore Annotations
@singleton — Single Instance
Creates one instance for the app lifetime.
dartimport 'package:injectable/injectable.dart'; class AuthService { String? _token; void login(String token) { _token = token; } bool get isLoggedIn => _token != null; } // Usage final authService = getIt<AuthService>(); authService.login('abc123');
@lazySingleton — Lazy Initialization
Creates singleton only when first accessed.
dartclass DatabaseService { DatabaseService() { print('Database initialized'); // Only prints when first used } }
@injectable — Factory (New Instance Each Time)
Creates a new instance on every request.
dartclass UserRepository { Future<User> fetchUser(String id) async { // Each call gets new repository instance } } // Usage final repo1 = getIt<UserRepository>(); // New instance final repo2 = getIt<UserRepository>(); // Another new instance
Constructor Injection
Injectable automatically resolves constructor dependencies.
dartclass ApiClient { final String baseUrl; ApiClient(('baseUrl') this.baseUrl); // Named parameter } class UserRepository { final ApiClient apiClient; UserRepository(this.apiClient); // Auto-injected Future<User> getUser(String id) async { return apiClient.get('/users/$id'); } } // Usage final userRepo = getIt<UserRepository>(); // ApiClient auto-injected
Named Dependencies
Use
text
@Nameddart// Register named strings abstract class AppModule { ('baseUrl') String get baseUrl => 'https://api.example.com ('apiKey') String get apiKey => 'secret_key_123'; } // Inject named dependencies class ApiService { final String baseUrl; final String apiKey; ApiService( ('baseUrl') this.baseUrl, ('apiKey') this.apiKey, ); }
Modules — Third-Party Dependencies
Use
text
@moduledartimport 'package:http/http.dart' as http; abstract class NetworkModule { http.Client get httpClient => http.Client(); Dio get dio => Dio(BaseOptions( baseUrl: 'https://api.example.com connectTimeout: 5000, )); } // Usage final client = getIt<http.Client>(); final dio = getIt<Dio>();
Environments
Register dependencies for specific environments (dev, prod, test).
dart// Development-only service (env: [Environment.dev]) class MockApiService implements ApiService { // Fake data for testing } // Production service (env: [Environment.prod]) class RealApiService implements ApiService { // Real API calls } // Initialize with environment void main() { configureDependencies(environment: Environment.prod); runApp(MyApp()); }
Async Dependencies
Register dependencies that require async initialization.
dartabstract class AsyncModule { // Wait for this before app starts Future<SharedPreferences> get prefs => SharedPreferences.getInstance(); } // In main.dart void main() async { WidgetsFlutterBinding.ensureInitialized(); await configureDependencies(); // Await async dependencies runApp(MyApp()); }
Dispose Pattern
dartclass DatabaseService implements Disposable { Database? _db; Future<void> init() async { _db = await openDatabase('app.db'); } void onDispose() { _db?.close(); // Cleanup } } // GetIt calls onDispose() when resetting getIt.reset(); // Calls onDispose on all Disposable services
Full Example
lib/services/auth_service.dart:
dartclass AuthService { String? _token; void login(String token) => _token = token; bool get isLoggedIn => _token != null; }
lib/repositories/user_repository.dart:
dartclass UserRepository { final ApiClient apiClient; UserRepository(this.apiClient); Future<User> getUser(String id) async { return apiClient.get('/users/$id'); } }
lib/blocs/user_bloc.dart:
dartclass UserBloc { final UserRepository repository; final AuthService authService; UserBloc(this.repository, this.authService); Future<User> loadUser(String id) async { if (!authService.isLoggedIn) throw Exception('Not logged in'); return repository.getUser(id); } }
lib/main.dart:
dartvoid main() { configureDependencies(); runApp(MyApp()); } class MyApp extends StatelessWidget { Widget build(BuildContext context) { final userBloc = getIt<UserBloc>(); // Fully injected return MaterialApp(home: HomeScreen(userBloc: userBloc)); } }
Build Runner Commands
bash# One-time generation flutter pub run build_runner build --delete-conflicting-outputs # Watch mode (auto-rebuild on changes) flutter pub run build_runner watch --delete-conflicting-outputs # Clean generated files flutter pub run build_runner clean
Comparison: get_it vs Injectable
| Feature | get_it (Manual) | Injectable |
|---|---|---|
| Registration | Manual code | Auto-generated |
| Boilerplate | High | Low ✅ |
| Type safety | Runtime | Compile-time ✅ |
| Dependencies | Manual wiring | Auto-resolved ✅ |
| Third-party classes | Manual | text |
Best Practices
dart// ✅ Use singleton for stateful services class AuthService { ... } // ✅ Use lazySingleton for expensive init class DatabaseService { ... } // ✅ Use injectable for stateless services class UserRepository { ... } // ✅ Use modules for third-party dependencies abstract class ThirdPartyModule { ... } // ✅ Named params for multiple implementations ('dev') class DevApiService implements ApiService { ... } // ❌ Don't use injectable for UI widgets (use Provider/Riverpod)
When to Use Injectable
- ✅ Large apps with many services
- ✅ Clean architecture (separate layers)
- ✅ Testability (mock dependencies easily)
- ✅ Type-safe DI (compile-time checks)
- ❌ Small apps (overkill — use Provider)
- ❌ Simple state (use Riverpod/BLoC)
Learn more: Injectable Package