WidjectContainer
Dependency Injection package for Flutter
Simple DI package to help structure your Flutter application in loosely coupled components.
Features
- Explicit constructor injection within scopes.
- No use of reflection/mirrors.
- Nested scope support to isolate dependencies by widget type.
- Async initialization of scoped dependencies.
Installation
-
Open your project’s pubspec.yaml file.
-
Add a git package dependency:
dependencies: # -------------------------- # # Your existing dependencies # # -------------------------- # widject_container: git: url: https://github.com/zlatancld/WidjectContainer
-
Execute
flutter pub get
.
Basic Usage
Create a scope connected to a widget and register the required types. The type references are automatically resolved when requested within the scope.
class AppScope extends Scope<AppWidget> {
AppScope(): super(null);
@override
void configure(ContainerRegister register) {
register.add((r) => HelloWorldProvider(), Lifetime.transient).as<MessageProvider>();
register.addWidget((r, key, _) => AppWidget(r.get(), key: key));
}
}
Where classes are:
abstract class MessageProvider {
String getMessage();
}
class HelloWorldProvider implements MessageProvider {
@override
String getMessage()
=> "Hello world!";
}
class AppWidget extends StatelessWidget {
final MessageProvider _messageProvider;
const AppWidget(this._messageProvider, {super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'WidjectContainer Demo',
home: Scaffold(
body: Text(_messageProvider.getMessage())
)
);
}
}
Example of scope usage in main function is:
void main() {
var app = AppScope().getWidget();
runApp(app);
}
Widget Provider
Use WidgetProvider
to instantiate widget types that have been registered within a scope. Dependencies are resolved and explicitly injected, as defined in the scope registration.
Example of registration:
class AppScope extends Scope<AppWidget> {
...
@override
void configure(ContainerRegister register) {
...
register.addWidget((r, key, _) => NewWidget(r.get(), args, key: key));
}
}
Example of usage through WidgetProvider
:
class AppWidget extends StatelessWidget {
final MessageProvider _messageProvider;
final WidgetProvider _widgetProvider;
const AppWidget(this._messageProvider, this._widgetProvider, {super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'WidjectContainer Demo',
home: Scaffold(
body: _widgetProvider.getWidget<NewWidget>())
)
);
}
}
Nested Scopes
Connect the instantiation of a widget to a new scope, registering types and inheriting references from ancestor scopes. This can be useful when creating a new screen that requires a whole new set of dependencies.
class ScreenScope extends Scope<ScreenWidget>{
ScreenScope(super.parentContainer);
@override
void configure(ContainerRegister register) {
register.addWidget((r, key, args) => ScreenWidget(...));
}
}
Where the scoped widget binding in the parent scope is:
class AppScope extends Scope<AppWidget> {
...
@override
void configure(ContainerRegister register) {
...
register.addScopedWidget((r, key, _) => ScreenScope(r));
}
}
Example of usage through WidgetProvider
:
class AppWidget extends StatelessWidget {
final MessageProvider _messageProvider;
final WidgetProvider _widgetProvider;
const AppWidget(this._messageProvider, this._widgetProvider, {super.key});
@override
Widget build(BuildContext context) {
return TextButton(
onPressed: () => _openChildWidget(context),
child: Text(_messageProvider.getMessage())
);
}
_openChildWidget(BuildContext context){
Navigator.push(context, MaterialPageRoute(
builder: (context) => _widgetProvider.getWidget<ScreenWidget>()));
}
}
Credits
WidjectContainer is inspired by:
Author
License
MIT