A lightweight, high-performance dependency injection container for Dart and Flutter with support for multiple lifecycles, scoping, and advanced features.
- ποΈ Multiple Lifecycles - Transient, Singleton, and Scoped dependencies
- π Hierarchical Containers - Parent-child container relationships
- π― Constructor Injection - Automatic dependency resolution
- π·οΈ Keyed Registrations - Multiple implementations of the same interface
- π¨ Decorators - Modify instances after creation
- π¦ Module System - Group related registrations
- β‘ High Performance - Optimized for speed and memory usage
- π§ͺ Container Validation - Detect configuration errors early
- π Disposable Support - Automatic resource cleanup
- π± Flutter Ready - Perfect for Flutter applications
Add drtdi to your pubspec.yaml:
dependencies:
drtdi: ^1.0.2import 'package:drtdi/drtdi.dart';
void main() {
// Create container
final container = DIContainer();
// Register dependencies
container.register<ApiService>((c) => ApiService(),
lifecycle: Lifecycle.singleton);
container.register<UserRepository>((c) => UserRepository(c.resolve<ApiService>()));
container.register<UserService>((c) => UserService(c.resolve<UserRepository>()));
// Resolve dependencies
final userService = container.resolve<UserService>();
// Validate container configuration
container.validate();
}import 'package:flutter/material.dart';
import 'package:drtdi/drtdi.dart';
void main() {
final container = DIContainer();
// Configure dependencies
container.register<ApiService>((c) => ApiService(), lifecycle: Lifecycle.singleton);
container.register<UserRepository>((c) => UserRepository(c.resolve<ApiService>()));
container.register<UserBloc>((c) => UserBloc(c.resolve<UserRepository>()));
runApp(MyApp(container: container));
}
class MyApp extends StatelessWidget {
final DIContainer container;
const MyApp({super.key, required this.container});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: ContainerProvider(
container: container,
child: const HomeScreen(),
),
);
}
}
class ContainerProvider extends InheritedWidget {
final DIContainer container;
const ContainerProvider({
super.key,
required this.container,
required super.child,
});
static ContainerProvider of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<ContainerProvider>()!;
}
@override
bool updateShouldNotify(ContainerProvider oldWidget) => false;
}
extension ContainerContext on BuildContext {
DIContainer get container => ContainerProvider.of(this).container;
}
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
final userBloc = context.container.resolve<UserBloc>();
return Scaffold(
appBar: AppBar(title: const Text('DRTDI Example')),
body: Center(
child: ElevatedButton(
onPressed: () => userBloc.loadUsers(),
child: const Text('Load Users'),
),
),
);
}
}New instance every time:
container.register<Service>((c) => Service(),
lifecycle: Lifecycle.transient);Single instance per container:
container.register<Service>((c) => Service(),
lifecycle: Lifecycle.singleton);Single instance per scope:
container.register<Service>((c) => Service(),
lifecycle: Lifecycle.scoped);
final scope = container.createScope();
final service = scope.resolve<Service>(); // New instanceRegister multiple implementations of the same interface:
container.register<Storage>((c) => FileStorage(), key: 'file');
container.register<Storage>((c) => CloudStorage(), key: 'cloud');
final fileStorage = container.resolve<Storage>(key: 'file');
final cloudStorage = container.resolve<Storage>(key: 'cloud');Modify instances after creation:
container.addDecorator<Service>((service) {
print('Service created at ${DateTime.now()}');
return LoggingService(service);
});Group related registrations:
class ApiModule implements DIModule {
@override
void configure(DIContainer container) {
container.register<ApiClient>((c) => ApiClient(),
lifecycle: Lifecycle.singleton);
container.register<UserApi>((c) => UserApi(c.resolve<ApiClient>()));
}
}
// Use module
container.addModule(ApiModule());Fluent API for container configuration:
final container = ContainerBuilder()
.register<Database>((c) => Database())
.asSingleton()
.register<UserRepository>((c) => UserRepository(c.resolve<Database>()))
.asScoped()
.addModule(ApiModule())
.build();final root = DIContainer();
root.register<Service>((c) => Service(), lifecycle: Lifecycle.scoped);
final scope1 = root.createScope();
final scope2 = root.createScope();
final service1 = scope1.resolve<Service>();
final service2 = scope2.resolve<Service>();
print(service1 == service2); // false - different scopesclass Database implements Disposable {
@override
void dispose() {
// Cleanup resources
print('Database disposed');
}
}
container.register<Database>((c) => Database(),
lifecycle: Lifecycle.singleton);
// Automatically disposed when container is disposed
container.dispose();DRTDI is optimized for performance:
// Benchmark Results
SimpleTransient(RunTime): 3.984322354236861 us.
DeepTransient(RunTime): 19.186531567342165 us.
Singleton(RunTime): 4.195086666666667 us.
MixedLifecycle(RunTime): 4.15242 us.
ScopeCreation(RunTime): 11.840668376323931 us.Easy testing with dependency mocking:
test('should test with mocked dependencies', () {
final container = DIContainer();
// Register mock implementation
container.register<ApiService>((c) => MockApiService());
container.register<UserRepository>((c) => UserRepository(c.resolve<ApiService>()));
final userRepository = container.resolve<UserRepository>();
// Test with mocked dependencies
expect(userRepository, isA<UserRepository>());
});DIContainer- Main dependency injection containerContainerScope- Scoped container for isolated dependency graphsDependencyRegistration- Individual dependency registrationLifecycle- Lifecycle enum (Transient, Singleton, Scoped)
register<T>()- Register a dependencyresolve<T>()- Resolve a dependencyresolveAll<T>()- Resolve all implementations of a typecreateScope()- Create a scoped containeraddModule()- Add a configuration modulevalidate()- Validate container configurationdispose()- Clean up resources
DRTDI provides clear error messages:
try {
container.resolve<UnregisteredService>();
} on RegistrationNotFoundException catch (e) {
print(e.message); // "No registration found for type UnregisteredService"
}
try {
container.resolve<ServiceA>(); // ServiceA depends on ServiceB which depends on ServiceA
} on CircularDependencyException catch (e) {
print(e.message); // "Circular dependency detected: ServiceA -> ServiceB -> ServiceA"
}We welcome contributions! Please see our Contributing Guide for details.
# Clone repository
git clone https://github.com/c0dwiz/drtdi.git
# Run tests
flutter test
# Run benchmarks
dart benchmark/performance_benchmark.dart
# Run examples
dart example/basic_usage.dartDRTDI is licensed under the MIT License. See LICENSE for details.
Thanks to all contributors and the Dart/Flutter community for inspiration and support.
Built with β€οΈ for the Dart and Flutter community
π Documentation β’ π Report Bug β’ π‘ Request Feature