4.3 KiB
DI Code Generation with Annotations (CherryPick)
CherryPick enables smart, fully-automated dependency injection (DI) for Dart/Flutter via annotations and code generation. This eliminates boilerplate and guarantees correctness—just annotate, run the generator, and use!
1. How does it work?
You annotate classes, fields, and modules using [cherrypick_annotations].
The [cherrypick_generator] processes these, generating code that registers your dependencies and wires up fields or modules.
You then run:
dart run build_runner build --delete-conflicting-outputs
— and use the generated files in your app.
2. Supported Annotations
| Annotation | Where | Purpose |
|---|---|---|
@injectable() |
class | Enables auto field injection; mixin will be generated |
@inject() |
field | Field will be injected automatically |
@scope() |
field/param | Use a named scope when resolving this dep |
@named() |
field/param | Bind/resolve a named interface implementation |
@module() |
class | Marks as a DI module (methods = providers) |
@provide |
method | Registers a type via this provider method |
@instance |
method | Registers a direct instance (like singleton/factory) |
@singleton |
method/class | The target is a singleton |
@params |
param | Accepts runtime/constructor params for providers |
You can combine annotations as needed for advanced use-cases.
3. Practical Examples
A. Field Injection (recommended for widgets/classes)
import 'package:cherrypick_annotations/cherrypick_annotations.dart';
@injectable()
class MyWidget with _$MyWidget { // the generated mixin
@inject()
late final AuthService auth;
@inject()
@scope('profile')
late final ProfileManager profile;
@inject()
@named('special')
late final ApiClient specialApi;
}
- After running build_runner, the mixin _$MyWidget is created.
- Call
MyWidget().injectFields();(method name may be_injector similar) to populate the fields!
B. Module Binding (recommended for global app services)
@module()
abstract class AppModule extends Module {
@singleton
AuthService provideAuth(Api api) => AuthService(api);
@provide
@named('logging')
Future<Logger> provideLogger(@params Map<String, dynamic> args) async => ...
}
- Providers can return async(
Future<T>) or sync. @singleton= one instance per scope.
4. Using the Generated Code
-
Add to your
pubspec.yaml:dependencies: cherrypick: any cherrypick_annotations: any dev_dependencies: cherrypick_generator: any build_runner: any -
Import generated files (e.g.
app_module.module.cherrypick.g.dart,your_class.inject.cherrypick.g.dart). -
Register modules:
final scope = openRootScope() ..installModules([$AppModule()]); -
For classes with auto-injected fields, mix in the generated mixin and call the injector:
final widget = MyWidget(); widget.injectFields(); // or use the mixin's helper -
All dependencies are now available and ready to use!
5. Advanced Features
- Named and Scoped dependencies: use
@named,@scopeon fields/methods and in resolve(). - Async support: Providers or injected fields can be Future (resolveAsync).
- Runtime parameters: Decorate a parameter with
@params, and useresolve<T>(params: ...). - Combining strategies: Mix field injection (
@injectable) and module/provider (@module+ methods) in one app.
6. Troubleshooting
- Make sure all dependencies are annotated, imports are correct, and run
build_runneron every code/DI change. - Errors in annotation usage (e.g.
@singletonon non-class/method) will be shown at build time. - Use the
.g.dartfiles directly—do not edit them by hand.
7. References
- Cherrypick Generator README (extended)
- Example:
examples/postly - API Reference