The injectable
package in Dart is used for Dependency Injection (DI). It helps automatically generate and manage dependencies, making code cleaner, modular, and testable.
What is Dependency Injection (DI)?
Dependency Injection is a design pattern where a class gets its dependencies from an external source instead of creating them itself.
For example, instead of doing this:
class UserService {
final ApiService apiService = ApiService(); // Directly creating instance ❌
}
With DI, you inject the dependency from outside:
class UserService {
final ApiService apiService; // Injected from outside ✅
UserService(this.apiService);
}
This makes the code loosely coupled, more maintainable, and testable.
Complete Example of Dependency Injection in Dart using injectable
and get_it
Example will show you step by step how dependency injection works in Dart/Flutter using the injectable
package.
Step 1: Add Dependencies
Add these in pubspec.yaml
:
dependencies:
get_it: ^7.2.0
injectable: ^2.3.0
dev_dependencies:
build_runner: ^2.4.0
injectable_generator: ^2.4.0
then run in terminal:
flutter pub get
Step 2: Create Dependency Injection Setup
Create a new file: di_setup.dart
import 'package:get_it/get_it.dart';
import 'package:injectable/injectable.dart';
import 'di_setup.config.dart';
final getIt = GetIt.instance; // Service locator instance
@InjectableInit()
void configureDependencies() => getIt.init(); // Initializes dependencies
Step 3: Create a Service Class
Create api_service.dart
import 'package:injectable/injectable.dart';
@LazySingleton() // Ensures only one instance is created when first needed
class ApiService {
ApiService() {
print("ApiService Created");
}
void fetchData() {
print("Fetching data from API...");
}
}
Step 4: Create a Repository That Uses This Service
Create user_repository.dart
import 'package:injectable/injectable.dart';
import 'api_service.dart';
@LazySingleton()
class UserRepository {
final ApiService _apiService;
UserRepository(this._apiService); // Dependency Injection in Constructor
void getUserData() {
_apiService.fetchData();
}
}
UserRepository
depends onApiService
, but it does not create it manually.Instead, injectable
injects an instance automatically.
Step 5: Generate Code for Dependencies
Run this command to auto-generate dependency registration:
flutter pub run build_runner build
This will create a file di_setup.config.dart
, which handles all dependency registration automatically.
Step 6: Use Dependencies in main.dart
Modify main.dart
to initialize and use dependencies:
import 'package:flutter/material.dart';
import 'di_setup.dart';
import 'user_repository.dart';
import 'package:get_it/get_it.dart';
void main() {
configureDependencies(); // Initialize dependencies
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomeScreen(),
);
}
}
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Retrieving instance of UserRepository
final userRepository = GetIt.instance<UserRepository>();
return Scaffold(
appBar: AppBar(title: Text("Dependency Injection Example")),
body: Center(
child: ElevatedButton(
onPressed: () {
userRepository.getUserData(); // Calls fetchData() from ApiService
},
child: Text("Fetch Data"),
),
),
);
}
}
How the Flow Works
1️⃣ main.dart
calls configureDependencies()
, which initializes all dependencies.
2️⃣ @LazySingleton()
ensures only one instance of ApiService
and UserRepository
is created.
3️⃣ In HomeScreen
, we retrieve UserRepository
from getIt
instead of creating it manually.
4️⃣ Clicking the button calls userRepository.getUserData()
, which internally calls fetchData()
from ApiService
.
5️⃣ The ApiService
prints “Fetching data from API…” to the console.
So, @LazySingleton()
in Dart (injectable
) – When, Why & Benefits
🔹 What is @LazySingleton()
?
@LazySingleton()
is an annotation in the injectable
package that registers a class as a singleton, but it will only be created when first needed (lazily).
🔍 Why Use @LazySingleton()
?
1️⃣ Saves Memory: Creates the object only when it’s first required, unlike @Singleton()
which initializes at app startup.
2️⃣ Improves Performance: Avoids unnecessary object creation at the beginning, reducing startup time.
3️⃣ Manages Global Services Efficiently: Ensures a single instance is used across the app, preventing redundant instances.
4️⃣ Better Dependency Management: Makes dependency injection smoother, reducing tight coupling.
5️⃣ Easier Testing: Enables injecting mock services during testing.
📅 When to Use @LazySingleton()
?
✅ When you need a single instance of a class but don’t want to create it until it’s first used.
✅ When using services, repositories, or APIs that are shared across multiple parts of the app.
✅ When an object’s creation is expensive, so delaying it saves resources.
🛑 When NOT to Use It?
❌ If the class should always be available at app start, use @Singleton()
.
❌ If the class needs to have multiple instances, use @Factory()
.