di_generator_build 1.6.2
di_generator_build: ^1.6.2 copied to clipboard
A powerful build runner package for automatic dependency injection code generation using GetIt in Flutter applications.
DI Generator Build #
A Flutter package that automatically generates dependency injection code for your classes using GetIt.
What This Package Does #
This package automatically creates dependency injection methods for any class you mark with the @AutoRegister annotation. It generates .g.dart files that contain methods to register and retrieve your classes from GetIt.
Key Benefit: Dependencies are only created and registered when you actually need them, not at startup!
How Lazy Loading Works #
Traditional Dependency Injection (Eager Loading): #
void main() {
// β All services created immediately at startup
registerUserService(); // Creates UserService now
registerProductService(); // Creates ProductService now
registerPaymentService(); // Creates PaymentService now
// App starts slow, uses more memory from the beginning
}
With This Package (Lazy Loading): #
void main() {
// β
No services created at startup - fast startup!
// Services exist only when you actually request them
}
void showUserProfile() {
// Only now is UserService created and registered
final userService = getUserService(); // Created on-demand! π―
// ProductService and PaymentService still don't exist
}
void showProducts() {
// Only now is ProductService created and registered
final productService = getProductService(); // Created on-demand! π―
}
Benefits of Lazy Loading #
- π Faster App Startup: No waiting for unused services to initialize
- πΎ Memory Efficient: Services exist only when needed
- π― On-Demand Creation: Dependencies created exactly when required
- β‘ Better Performance: App feels snappy and responsive
- π Smart Resource Management: Resources allocated only when necessary
β¨ Features #
- Intuitive Annotations: Use clear annotations like
@Factory,@Singleton,@LazySingleton - Automatic Code Generation: Generates dependency injection methods automatically
- GetIt Integration: Seamlessly integrates with the GetIt service locator
- Async Support: Full support for async dependency initialization
- Performance Optimized: Efficient dependency resolution with GetIt integration
π Quick Start #
1. Add Dependencies #
Add to your pubspec.yaml:
dependencies:
get_it: ^8.2.0
di_generator_build: ^1.6.2
dev_dependencies:
build_runner: ^2.7.0
2. Annotate Your Classes #
import 'package:di_generator_build/annotations.dart';
@RegisterSingleton()
class AppConfig {
final String apiUrl;
final String apiKey;
AppConfig({required this.apiUrl, required this.apiKey});
}
@RegisterLazySingleton()
class HttpClient {
final AppConfig _config;
HttpClient(this._config);
Future<String> get(String url) async {
// HTTP client implementation
}
}
@RegisterFactory()
class EmailService {
final HttpClient _httpClient;
final String _apiKey;
EmailService(this._httpClient, {required String apiKey});
Future<void> sendEmail(String to, String subject, String body) async {
// Email sending logic
}
}
3. Generate Code #
Run the code generator:
dart run build_runner build
4. Use Generated Methods #
import 'your_file.g.dart';
void main() {
// Get services using generated methods (no parameters needed)
final config = getConfigService();
final client = getNetworkService(); // Automatically gets ConfigService dependency
final authService = getAuthService(); // Gets NetworkService dependency automatically
}
π Available Annotations #
Synchronous Annotations #
- @RegisterFactory(): Creates new instance each time
- @RegisterSingleton(): Creates instance immediately and reuses it
- @RegisterLazySingleton(): Creates instance on first use, then reuses it
Asynchronous Annotations #
- @RegisterAsyncFactory(): Creates new async instance each time
- @RegisterAsyncSingleton(): Creates async instance immediately and reuses it
- @RegisterAsyncLazySingleton(): Creates async instance on first use, then reuses it
π― Key Benefits #
Performance Optimization #
Lazy singletons create dependencies only when first requested, reducing startup time and memory usage.
Memory Efficiency #
- Factories: Create new instances each time (useful for stateless services)
- Singletons: Reuse the same instance (useful for stateful services)
- Lazy Singletons: Create on first use, then reuse (best of both worlds)
Async Support #
Handle complex initialization scenarios with async support for database connections, API clients, and more.
Clean Architecture #
Separate dependency creation from business logic, making your code more testable and maintainable.
Automatic Dependency Resolution #
Dependencies are automatically injected based on constructor parameters, reducing boilerplate code.
Cross-File Dependencies #
When services depend on each other across different files, import the generated .g.dart files in your main application code:
// In your main.dart or di_setup.dart
import 'services/app_config.g.dart';
import 'services/http_client.g.dart';
import 'services/email_service.g.dart';
// ... import other generated files as needed
Automatic Parameter Handling #
The generated methods automatically handle all parameters with sensible defaults:
- Required parameters: Use meaningful defaults based on parameter names (e.g.,
apiKeyβ"default-api-key") - Optional parameters: Use the default values defined in the constructor
- Dependencies: Automatically resolved from GetIt service locator
// No need to pass parameters - everything is handled automatically
final authService = getAuthService(); // Uses default values for apiKey, secretKey, tokenExpiry
final userService = await getUserService(); // All dependencies resolved automatically
Important Note About Cross-File Dependencies #
The generated .g.dart files are independent and don't automatically import each other. This is by design to keep the package generic. You need to:
- Import all the necessary
.g.dartfiles in your main application code - Call the dependency methods with their required parameters before using dependent services
- The GetIt service locator will handle the dependency resolution
Example:
// In your main.dart or di_setup.dart
import 'services/config_service.g.dart';
import 'services/network_service.g.dart';
import 'services/auth_service.g.dart';
import 'services/storage_service.g.dart';
import 'services/user_repository.g.dart';
import 'services/user_service.g.dart';
import 'services/notification_service.g.dart';
void main() async {
// Set up dependencies in the correct order (no parameters needed)
final config = getConfigService();
final network = getNetworkService(); // Uses the registered ConfigService
final auth = getAuthService(); // Uses the registered NetworkService
final storage = await getStorageService(); // Uses default connection string
final userRepo = await getUserRepository(); // Uses the registered StorageService
final userService = await getUserService(); // Uses the registered UserRepository and AuthService
final notification = await getNotificationService(); // Uses the registered NetworkService
}
Best Practices for Dependency Management #
- Order Matters: Always call dependencies before the services that depend on them
- Required Parameters: Provide required parameters when calling factory services
- Async Services: Use
awaitfor async services - Singleton vs Factory: Understand when to use each type:
- Singleton: Use for configuration, shared state
- Lazy Singleton: Use for expensive services that should be shared
- Factory: Use for services that need different parameters each time
π Examples #
Basic Service #
@RegisterSingleton()
class UserService {
final UserRepository _repository;
UserService(this._repository);
Future<User> getUser(String id) async {
return await _repository.findById(id);
}
}
Service with Parameters #
@RegisterFactory()
class EmailService {
final String _apiKey;
final EmailProvider _provider;
EmailService(this._provider, [this._apiKey = 'default-key']);
Future<void> sendEmail(String to, String subject, String body) async {
// Email sending logic
}
}
Async Service #
@RegisterAsyncLazySingleton()
class DatabaseService {
final String _connectionString;
late final Database _database;
DatabaseService(this._connectionString);
Future<void> initialize() async {
_database = await Database.connect(_connectionString);
}
Future<QueryResult> query(String sql) async {
return await _database.execute(sql);
}
}
π Generated Code #
The package automatically generates getter methods for each annotated class:
// For @RegisterSingleton() class UserService
UserService getUserService() {
return GetIt.instance.getOrRegister<UserService>(
() => UserService(getUserRepository()), RegisterAs.singleton);
}
// For @RegisterAsyncFactory() class DatabaseService
Future<DatabaseService> getDatabaseService({String connectionString = 'default'}) async {
return await GetIt.instance.getOrRegisterAsync<DatabaseService>(
() async => DatabaseService(connectionString), RegisterAs.factoryAsync);
}
π§ͺ Testing #
The generated code integrates seamlessly with GetIt, making it easy to mock dependencies in tests:
void main() {
setUp(() {
// Register mock dependencies
GetIt.instance.registerSingleton<UserRepository>(MockUserRepository());
});
tearDown(() {
GetIt.instance.reset();
});
test('should get user service', () {
final userService = getUserService();
expect(userService, isA<UserService>());
});
}
π¦ Installation #
dart pub add di_generator_build
dart pub add build_runner --dev
π€ Contributing #
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
Development Setup #
- Clone the repository
- Install dependencies:
dart pub get - Run tests:
dart test - Make your changes
- Submit a pull request
π License #
This project is licensed under the MIT License - see the LICENSE file for details.
π Acknowledgments #
- Built on top of the excellent GetIt package
- Uses build_runner for code generation
- source_gen: Source code generation utilities
π Support #
If you have any questions or need help, please:
- Check the examples directory
- Review the tests for usage patterns
- Open an issue on GitHub
- Check the documentation
Happy coding! π