DI Generator Build

Pub Version Dart Version Flutter Version License Build Status Code Coverage Issues

Dependency Injection Code Generation GetIt Integration Async Support Lazy Loading Performance

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:

  1. Import all the necessary .g.dart files in your main application code
  2. Call the dependency methods with their required parameters before using dependent services
  3. 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

  1. Order Matters: Always call dependencies before the services that depend on them
  2. Required Parameters: Provide required parameters when calling factory services
  3. Async Services: Use await for async services
  4. 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

  1. Clone the repository
  2. Install dependencies: dart pub get
  3. Run tests: dart test
  4. Make your changes
  5. Submit a pull request

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

πŸ™ Acknowledgments

πŸ“ž Support

If you have any questions or need help, please:

  1. Check the examples directory
  2. Review the tests for usage patterns
  3. Open an issue on GitHub
  4. Check the documentation

Happy coding! πŸŽ‰

Libraries

annotations
builder
di_generator_build
A powerful build runner package for automatic dependency injection code generation using GetIt in Flutter applications.
get_it_extension