dire_di library
Dire DI - Spring-like Dependency Injection for Dart
A powerful dependency injection framework that brings Spring-like features to Dart, with Flutter compatibility using code generation instead of dart:mirrors.
Features
- Spring-like Annotations:
@Service,@Repository,@Component,@Controller,@UseCase,@DataSource - Constructor Injection: Automatic dependency resolution via constructors
- Qualifier Support: Use named instances for 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 - Flutter Compatible: Uses code generation instead of dart:mirrors
- Same API: Maintains the original DireContainer API you know and love
Quick Start
Add to your pubspec.yaml:
dependencies:
dire_di: ^1.0.3
dev_dependencies:
build_runner: ^2.4.7
Annotate your classes (same as before):
import 'package:dire_di/dire_di.dart';
@Service()
class UserService {
final UserRepository userRepository;
UserService(this.userRepository); // Constructor injection
Future<User> getUser(int id) {
return userRepository.findById(id);
}
}
@Repository()
class UserRepository {
Future<User> findById(int id) async {
// Implementation here
}
}
Run code generation:
flutter pub get
flutter pub run build_runner build
Use in your app (same API as before):
void main() async {
final container = DireContainer();
await container.scan(); // Now uses generated code instead of mirrors
final userService = container.get<UserService>();
// userService.userRepository is automatically injected!
}
Migration from Mirrors
If you're migrating from the old mirrors-based version:
-
Change from field injection to constructor injection:
// Before (mirrors) @Service() class UserService { @Autowired() late UserRepository userRepository; } // After (code generation) @Service() class UserService { final UserRepository userRepository; UserService(this.userRepository); } -
Add build_runner and run code generation:
flutter pub add --dev build_runner flutter pub run build_runner build -
Same API - No changes to your main() or container usage!
Advanced Usage
Named Instances (Qualifiers)
@Service()
@Qualifier('primary')
class PrimaryUserService extends UserService {
// Implementation
}
// Usage - same as before
final primary = container.get<UserService>('primary');
Singletons vs Prototype
@Service()
@Singleton() // Single instance
class DatabaseConnection {
// Implementation
}
@Service()
@Prototype() // New instance each time
class RequestHandler {
// Implementation
}
Environment-specific Beans
@Service()
@Profile('dev')
class DevUserService extends UserService {
// Development implementation
}
@Service()
@Profile('prod')
class ProdUserService extends UserService {
// Production implementation
}
Benefits of Code Generation
- ✅ Flutter Compatible - No dart:mirrors dependency
- ✅ Better Performance - No runtime reflection overhead
- ✅ Compile-time Safety - Dependency errors caught at build time
- ✅ Tree Shaking - Only used dependencies in final bundle
- ✅ Same API - Your existing code mostly unchanged
- ✅ IDE Support - Better autocomplete and refactoring
@Service()
@Qualifier('primary')
class PrimaryUserService implements UserService { }
@Service()
@Qualifier('secondary')
class SecondaryUserService implements UserService { }
@Component()
class UserController {
@Autowired()
@Qualifier('primary')
late UserService userService;
}
Configuration Classes
@Configuration()
class DatabaseConfig {
@Bean()
@Singleton()
Database createDatabase() {
return Database(connectionString: 'localhost:5432');
}
}
Profiles
@Service()
@Profile('development')
class DevUserService implements UserService { }
@Service()
@Profile('production')
class ProdUserService implements UserService { }
Classes
- Autowired
- Autowired annotation for automatic dependency injection
- Bean
- Bean method annotation
- BeanDefinition
- Represents a bean definition in the DI container
- Component
- Core component annotation - base for all stereotypes
- Conditional
- Base class for all conditional annotations
- ConditionalOnBean
- Register bean only if a bean of the specified type exists
- ConditionalOnClass
- Register bean only if the specified class is present on the classpath
- ConditionalOnMissingBean
- Register bean only if no bean of the specified type exists
- ConditionalOnProperty
- Register bean only if the specified property exists and matches the value
- Configuration
- Configuration class annotation
- Controller
- Controller annotation for marking web controllers and similar components. Controllers typically handle HTTP requests, user input, or other external interfaces.
- DataSource
- DataSource layer annotation - indicates data source components
- DireContainer
- The main dependency injection container for Dire DI
- DireDiEntryPoint
- Marks a file as the entry point for DI code generation.
- FieldDefinition
- Represents a field that needs injection
- InjectionContext
- Injection context for managing bean creation and dependency resolution
- ParameterDefinition
- Represents a constructor parameter that needs injection
- Profile
- Profile annotation for environment-specific beans
- Prototype
- Convenience annotation for prototype scope
- Qualifier
- Qualifier annotation for specific bean selection
- Repository
- Repository layer annotation - indicates data access layer components
- Scope
- Scope annotation for controlling bean lifecycle
- Service
- Service layer annotation - indicates that this class provides business logic
- Singleton
- Convenience annotation for singleton scope
- UseCase
- UseCase layer annotation - indicates application use case components
Enums
- ScopeType
- Enumeration of bean scope types
Mixins
- DiCore
- Mixin for StatefulWidget States that provides easy access to dependency injection.
Functions
-
areTypesEquivalent(
Type type1, Type type2) → bool - Checks if two types are equivalent for dependency injection purposes
-
convertStringToType<
T> (String value, Type targetType) → T? - Converts a string to the appropriate type
-
getDefaultValue(
Type type) → Object? - Gets a default value for a type
-
getGenericTypeArguments(
Type type) → List< Type> - Extracts generic type arguments from a collection type
-
getNonNullableType(
Type type) → Type - Gets the non-nullable version of a type
-
getTypeName(
Type type) → String - Gets the simple name of a type (without library prefix)
-
isAssignableFrom(
Type target, Type source) → bool - Checks if a type is assignable from another type
-
isCollectionType(
Type type) → bool - Checks if a type is a collection type
-
isNullableType(
Type type) → bool - Checks if a type is nullable
-
isPrimitiveType(
Type type) → bool - Checks if a type is a primitive type
Exceptions / Errors
- BeanCreationException
- Exception thrown when bean creation fails
- BeanNotFoundException
- Exception thrown when a required bean cannot be found
- CircularDependencyException
- Exception thrown when circular dependencies are detected
- ConditionalEvaluationException
- Exception thrown when conditional evaluation fails
- ContainerInitializationException
- Exception thrown when container initialization fails
- DireException
- Base exception class for all Dire DI related exceptions
- InjectionException
- Exception thrown when dependency injection fails
- InvalidBeanConfigurationException
- Exception thrown when bean configuration is invalid
- MultipleBeanFoundException
- Exception thrown when multiple beans are found and no primary is specified