Decorator Gen
A Dart code generator that automatically creates the decorator pattern for your Dart code.
The builder generates code if it finds classes with annotations from the decorator_annotation package.
Features
- Generate complete decorator classes with a single
@Decorator()
annotation- Forward all public members of the source class
- Works with generics, records, nested types, etc.
- Object methods can be forwarded globally or per class
Installation
Add the following to your pubspec.yaml
:
dependencies:
decorator_annotation: ^latest
dev_dependencies:
decorator_gen: ^latest
build_runner: any
Usage
1. Annotate Your Class And Include The Part Directive
import 'package:decorator_annotation/decorator_annotation.dart';
part 'example.g.dart'; // This part directive is necessary for code generation
@Decorator() // Add this annotation to generate a decorator for this class
class MyService {
final String name;
MyService(this.name);
String greet(String message) => 'Hello $message from $name';
Future<String> asyncOperation() async {
await Future.delayed(const Duration(seconds: 1));
return 'Completed';
}
}
// Generates: MyServiceDecorator in example.g.dart
class MyServiceDecorator implements MyService {
final MyService myService;
MyServiceDecorator({required this.myService});
@override
String greet(String message) {
return myService.greet(message);
}
@override
Future<String> asyncOperation() {
return myService.asyncOperation();
}
@override
String get name => myService.name;
}
2. Run Code Generation
dart run build_runner build
3. Use Your Generated Decorator
// Generated: MyServiceDecorator
class LoggingServiceDecorator extends MyServiceDecorator {
LoggingServiceDecorator({required super.myService});
@override
String greet(String message) {
print('Calling greet with: $message');
final result = super.greet(message);
print('greet returned: $result');
return result;
}
@override
Future<String> asyncOperation() async {
print('Starting async operation');
final result = await super.asyncOperation();
print('Async operation completed');
return result;
}
}
// Usage
final service = MyService('TestService');
final decorator = LoggingServiceDecorator(myService: service);
print(decorator.greet('World')); // Logs and returns result
Supported Features
Generics (with Bounds)
@Decorator()
class GenericService<T extends Comparable<T>> {
T process(T value) => /* implementation */
}
// Generates: GenericServiceDecorator<T extends Comparable<T>>
Mixins
mixin SimpleMixin {
void mixinMethod() => /* implementation */
}
@Decorator()
class MixinService with SimpleMixin {
void ownMethod() => /* implementation */
}
// Generates: MixinServiceDecorator having mixinMethod and ownMethod
Object methods and Annotation-level Forwarding
You can control which Object methods are forwarded per class using parameters on the @Decorator()
annotation:
@Decorator(
forwardToString: true, // forward toString (default: true)
forwardEquals: false, // do NOT forward == (default: true)
forwardHashCode: true, // forward hashCode (default: true)
forwardRuntimeType: true, // forward runtimeType (default: false)
forwardNoSuchMethod: false, // do NOT forward noSuchMethod (default: false)
)
class CustomService {
final String name;
CustomService(this.name);
@override
String toString() => 'CustomService($name)';
@override
bool operator ==(Object other) => other is CustomService && other.name == name;
@override
int get hashCode => name.hashCode;
@override
Type get runtimeType => super.runtimeType;
@override
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
String processData(String data) => 'Processing: $data';
}
// Only toString, hashCode, and runtimeType will be forwarded in the generated decorator.
Operators
@Decorator()
class OperatorService {
int operator +(int other) => /* implementation */
bool operator ==(Object other) => /* implementation */
int operator [](int index) => /* implementation */
}
// Generates: OperatorServiceDecorator with forwarded operators
Parameters (positional, named, optional, combined)
@Decorator()
class ParameterService {
void positionalNamed(int a, {String? b, double c = 0.0}) => /* implementation */
void positionalOptional(int a, [String? b, double c = 0.0]) => /* implementation */
}
// Generates: ParameterServiceDecorator with all parameter types handled
Records
@Decorator()
class RecordService {
(int, String) getTuple() => (42, 'Hello World!');
// With named fields
void setRecord(({int foo, String bar}) record) => /* implementation */
}
// Generates: RecordServiceDecorator with tuple handling
Forwarding Object Methods: Global Defaults
The generator can generate code that forwards the Object methods.
The following Object methods are included by default:
toString
==
hashCode
The following Object methods are excluded by default:
runtimeType
noSuchMethod
You can customize the global default behavior in your build.yaml
file:
targets:
$default:
builders:
decorator_gen:
enabled: true
options:
forward_object_method:
toString: true # Defaults to true
"==": true # Defaults to true
hashCode: true # Defaults to true
runtimeType: false # Defaults to false
noSuchMethod: false # Defaults to false
FAQ
Private members
Private members (those starting with _
) are included in the generated decorators.
This is required by the Dart compiler. All accessible members must be implemented.
Static members
Static members are not included in the generated decorators.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the MIT License.