force_inject 0.4.0
force_inject: ^0.4.0 copied to clipboard
A zero-dependency DI container for Dart, inspired by .NET Core. This is the Way.
π ForceInject #
π οΈ A lightweight, zero runtime dependency Dependency Injection (DI) container for Dart.
π Inspired by .NET Core and powered by the Force.
This is the Way.
β¨ Features #
- Constructor-based dependency injection
- Support for
singleton
,transient
, andscoped
lifetimes - Named & tagged service resolution
- Scoped service overrides for testing and previews
- No code generation, no mirrors
- No runtime dependencies β pure Dart
- Fully compatible with Flutter, CLI, and server
- Inspired by C#/.NET Core DI patterns
- Lightweight, test-friendly, and easy to integrate
βοΈ Dependencies #
ForceInject is designed to have no runtime dependencies.
We use the Dart team's official meta
package only for static annotations like @protected
and @visibleForTesting
.
This ensures:
- β No runtime or platform bloat
- β Full IDE and analyzer support
- β Maximum compatibility with Flutter, backend, and CLI
π Getting Started #
1. Register your services #
final services = ServiceCollection();
services.addSingleton<ILogger, ConsoleLogger>();
services.addTransient<UserService, UserService>();
ServiceProvider.registerConstructor<ConsoleLogger>(() => ConsoleLogger(), []);
ServiceProvider.registerConstructor<UserService>(
(ILogger logger) => UserService(logger),
[ILogger],
);
2. Build the provider #
final provider = services.buildServiceProvider();
3. Resolve services #
final userService = provider.get<UserService>();
userService.sayHello(); // [LOG]: Hello from UserService!
Need a per-screen or per-request services? Create a ServiceScope
: #
final scope = provider.createScope();
final viewModel = scope.get<MyViewModel>();
scope.dispose(); // clean up when done
βοΈ Lifetimes #
Type | Description |
---|---|
singleton |
One shared instance across the application |
transient |
A new instance is created every time |
scoped |
One instance per ServiceScope β ideal for screen/request lifetimes |
π§ Advanced Usage #
π Scoped Overrides #
You can override services inside a scope to inject mocks or alternate implementations:
final scope = provider.createScope();
scope.overrideService<Logger>(FakeLogger());
final logger = scope.get<Logger>(); // Returns FakeLogger
You can also provide overrides on creation:
final scope = provider.createScope(overrides: {
Logger: FakeLogger(),
});
This is great for testing, previews, or A/B environments.
π§ͺ Testing & Mocks #
Check out test/
for unit tests and real-world setup examples.
You can also override services dynamically using .overrideService()
for fine-grained control.
π¦ Why ForceInject? #
Dart is an elegant language, but backend structure is still evolving.
ForceInject gives you the power of .NETβs DI model, but in pure Dart β making your code modular, testable, and maintainable.
No magic. No mirrors. Just clean, focused architecture.
π¬ "When your services are confused, inject them you must." β Yodart
π§ͺ Examples #
This package includes several complete examples you can explore:
-
π§±
minimal_dart_di
A simple Dart CLI app using ForceInject with singleton and transient services. -
π±
minimal_flutter_di
A clean Flutter app using scoped DI and aValueNotifier
-based ViewModel. -
π±Scoped Widget DI A clean Flutter app using Scoped Widget DI.
-
π±Async ViewModel DI A clean Flutter app using Scoped Widget DI with life cycle control with async initialization.
To run the Flutter example:
cd example/minimal_flutter_di
flutter create .
flutter pub get
flutter run
Read the README.md inside the minimal_flutter_di project for more details.
Support me (@diegomgarcia) with Patreon #
Or make a donation buying me a coffee:
You can also show support by showing on your repository that you use this lib on it with a direct link to it.
π License #
MIT β Free to use, modify, or extend.
May the DI be with you. βοΈ