dire_di_flutter 1.0.0
dire_di_flutter: ^1.0.0 copied to clipboard
A Spring-like dependency injection framework for Dart with code generation for Flutter compatibility.
Dire DI #
A Spring-like dependency injection framework for Dart with auto-wiring capabilities using mirrors.
Features #
- Spring-like Annotations:
@Service,@Repository,@Component,@Controller - Auto-wiring: Automatic dependency resolution using
@Autowired - Qualifier Support: Use
@Qualifierfor specific bean selection - Singleton and Prototype Scopes: Control object lifecycle
- Conditional Registration:
@ConditionalOnProperty,@ConditionalOnClass - Profile Support:
@Profilefor environment-specific beans - Configuration Classes:
@Configurationand@Beanfor manual setup
Installation #
Add this to your package's pubspec.yaml file:
dependencies:
dire_di: ^1.0.2
Quick Start #
1. Define Your Services #
import 'package:dire_di/dire_di.dart';
@Service()
class UserService {
@Autowired()
UserRepository? userRepository;
@Autowired()
EmailService? emailService;
Future<User> getUser(int id) {
return userRepository!.findById(id);
}
}
@Repository()
class UserRepository {
Future<User> findById(int id) async {
// Database access logic here
return User(id: id, name: 'John Doe');
}
}
@Service()
class EmailService {
void sendEmail(String to, String subject, String body) {
print('Sending email to $to: $subject');
}
}
2. Initialize the Container #
void main() async {
final container = DireContainer();
await container.scan(); // Auto-discover and register components
final userService = container.get<UserService>();
final user = await userService.getUser(1);
print('User: ${user.name}');
}
Advanced Usage #
Qualifiers #
When you have multiple implementations of the same interface:
@Service()
@Qualifier('primary')
class PrimaryUserService implements UserService {
// Implementation
}
@Service()
@Qualifier('secondary')
class SecondaryUserService implements UserService {
// Implementation
}
@Component()
class UserController {
@Autowired()
@Qualifier('primary')
UserService? userService;
}
Configuration Classes #
For complex bean setup:
@Configuration()
class DatabaseConfig {
@Bean()
@Singleton()
Database createDatabase() {
return Database(connectionString: Environment.dbUrl);
}
@Bean()
@Qualifier('cache')
Cache createCache() {
return RedisCache();
}
}
Profiles #
Environment-specific configurations:
@Service()
@Profile('development')
class DevEmailService implements EmailService {
void sendEmail(String to, String subject, String body) {
print('DEV: Sending email to $to: $subject');
}
}
@Service()
@Profile('production')
class ProdEmailService implements EmailService {
void sendEmail(String to, String subject, String body) {
// Real email sending logic
}
}
// Set active profiles
final container = DireContainer(activeProfiles: ['development']);
Conditional Beans #
Register beans based on conditions:
@Service()
@ConditionalOnProperty(
name: 'feature.email.enabled',
havingValue: 'true'
)
class EmailService {
// Only registered if feature.email.enabled=true
}
@Service()
@ConditionalOnClass('package:sqflite/sqflite.dart')
class SQLiteUserRepository implements UserRepository {
// Only registered if sqflite package is available
}
Important Note about Field Types #
Due to Dart's mirror system limitations, late fields may not work properly with dependency injection. Use nullable fields instead:
// ❌ Problematic with mirrors
@Service()
class MyService {
@Autowired()
late UserRepository repository;
}
// ✅ Recommended approach
@Service()
class MyService {
@Autowired()
UserRepository? repository;
}
Comparison with Other DI Solutions #
vs get_it + injectable #
get_it + injectable:
@injectable
class UserService {
UserService(this.userRepository);
final UserRepository userRepository;
}
// Requires build_runner
// No field injection
// Manual registration often needed
dire_di:
@Service()
class UserService {
@Autowired()
late UserRepository userRepository; // Field injection!
}
// No build_runner needed
// Spring-like annotations
// Auto-discovery with reflection
Key Advantages #
- No Code Generation: Uses mirrors for runtime reflection
- Field Injection: Direct field injection like Spring
- Auto-Discovery: Automatic component scanning
- Spring Familiarity: Same annotations as Spring Framework
- Rich Conditional Support: Extensive conditional registration
- Profile Management: Environment-specific configurations
API Reference #
Annotations #
@Component()- Base component annotation@Service()- Service layer components@Repository()- Data access layer components@Controller()- Presentation layer components@Configuration()- Configuration classes@Bean()- Bean factory methods@Autowired()- Dependency injection marker@Qualifier(name)- Bean qualification@Singleton()- Singleton scope@Prototype()- Prototype scope@Profile(profiles)- Profile-specific beans@ConditionalOnProperty()- Property-based conditions@ConditionalOnClass()- Class-based conditions
Container Methods #
final container = DireContainer();
// Initialize
await container.scan();
// Get beans
final service = container.get<UserService>();
final qualifiedService = container.get<UserService>('qualifier');
// Check existence
bool exists = container.contains<UserService>();
// Get all instances
List<UserService> services = container.getAll<UserService>();
// Manual registration
container.register<UserService>(() => UserService());
container.registerInstance<UserService>(userServiceInstance);
Best Practices #
- Use Specific Annotations: Prefer
@Service,@Repositoryover generic@Component - Qualify Multiple Implementations: Use
@Qualifierwhen you have multiple beans of same type - Profile Organization: Group environment-specific beans with
@Profile - Lazy Initialization: Use prototype scope for heavy objects when appropriate
- Constructor vs Field Injection: Field injection is simpler, constructor injection is more testable
Limitations #
- Mirrors Dependency: Requires dart:mirrors (not available in Flutter web)
- Runtime Overhead: Reflection has performance cost compared to code generation
- Tree Shaking: May prevent dead code elimination in some cases
Contributing #
Contributions are welcome! Please feel free to submit a Pull Request.
License #
This project is licensed under the MIT License - see the LICENSE file for details.