introduction
Refuse to repeat work and pursue efficiency and performance. Dart based annotation processing librarysource_gen, let’s take a look at how to generate code using custom annotations.
Add reference
source_gen: used to parse annotations
build_runner: used to generate code
dependencies:
flutter:
sdk: flutter
source_gen:
build_runner:
-
Step 1: Customize annotation
New test_ Anotaion.dart file, the class parammetadata brought in by customization is used as annotation
//Bring in the user-defined annotation of the parameter
class ParamMetadata {
final String name;
final int id;
const ParamMetadata(this.name, this.id);
}
Important: the construction method of annotation class requires const modification
-
Step 2: use annotations
After the annotation class is defined, create a new file model_ Class. Dart, create an entity class, and useParamMetadata
Annotate:
@ParamMetadata("annotationClass", 1)
class ClassModel {
final String className;
final List<Student> members;
ClassModel(this.className, this.members);
void funPrint(String content) {
print("className = $className ; members = ${members.toString()}");
}
}
class Student {
final String name;
const Student(this.name);
}
-
Step 3: parse the annotation and generate the code
- To create a parser, we need to inherit the generatorforannotation < T > generic t as the annotation class to be parsed, that is, our customized parammetadata.
import 'package:source_gen/source_gen.dart';
import 'package:test_anotation/anotation/test_anotation.dart';
import 'package:analyzer/dart/element/element.dart' as e;
import 'package:build/build.dart';
import 'package:test_anotation/generator/temp_code.dart';
class TestGenerator extends GeneratorForAnnotation<ParamMetadata> {
@override
generateForAnnotatedElement(e.Element element, ConstantReader annotation, BuildStep buildStep) {
analyseBuildStep(buildStep);
analyseAnnotation(annotation);
return tempCode(element.name, analyseElement(element));
}
//Analysis element
String analyseElement(e.Element element) {
print("ElementKind : ${element.kind.name} \n");
switch (element.kind) {
case e.ElementKind.CLASS:
//Comments are used on classes
return _analyseElementForClass(element as e.ClassElement);
case e.ElementKind.FUNCTION:
//Annotations are used on methods
return _analyseElementForMethod(element as e.FunctionElement);
default:
return "";
}
}
//Annotations are used on classes
String _analyseElementForClass(e.ClassElement classElement) {
Var fieldstr = "the intercepted member fields in class are: \ n";
for (var e in classElement.fields) {
fieldStr += " ${e.declaration}\n";
}
Var methodstr = "member methods intercepted in class: \ n";
for (var e in classElement.methods) {
methodStr += " ${e.declaration}\n";
}
return fieldStr + "\n" + methodStr;
}
//Annotations are used on methods
String _analyseElementForMethod(e.FunctionElement methodElement) {
var result =
"Method name: ${methodelement. Name}, method parameters: ${methodelement. Parameters [0]. Declaration} \ n";
return result;
}
//Analysis annotation transfer parameter
void analyseAnnotation(ConstantReader annotation) {
print("analyseAnnotation \n");
print("params - name : ${annotation.read("name")}\n");
print("params - id : ${annotation.read("id")}\n");
}
//Analyze the input and output information of the build
void analyseBuildStep(BuildStep buildStep) {
Print ("current input source: ${buildstep. Inputid. Tostring()} \ n");
}
}
//Templates for creating files
tempCode(String className, String content) {
return """
class ${className}APT {
/**
$content
*/
}
""";
}
Override generateforannotatedelement to intercept annotations of parammetadata type:
-
analyseElement()
: get the analysis elements, distinguish the element types and handle them separately. See the code annotation for details. In the demo, the parsed content is sealed into a string, and the text result is finally displayed on the generated code file. -
analyseBuildStep()
: get the input and output information of the build, for example, it will be printed in the demo
Current input source: Test_ anotation|lib/model/model_ class.dart -
analyseAnnotation()
: get the parameters of the analysis annotation, such as the name and ID parameter fields in our annotation construction method
A small partner should notice that it is used in the import referenceas
Keyword. When using an element, it is directly referenced by an alias because the element has duplicate names in different librariesas
Keyword processing error red.
import 'package:analyzer/dart/element/element.dart' as e;
...
generateForAnnotatedElement(e.Element element, ConstantReader annotation, BuildStep buildStep)
...
We know that dart: mirror is disabled in shutter, so reflection cannot be used. Therefore, we can only trigger the operation during compilation through the command. Before executing the command, we need to implement another step to create the configuration file:
- Create a global method with a return type of Builder: create a new file (arbitrary name), such as test_ Build.dart, as follows:
import 'package:source_gen/source_gen.dart';
import 'package:build/build.dart' as build;
import 'package:test_anotation/generator/annotation_class_generator.dart';
build.Builder testBuilder(build.BuilderOptions options) => LibraryBuilder(TestGenerator());
//Testgenerator is our parser
- Create a build.yaml file in the root directory of the project. Its significance is to configure various parameters of the Builder:
builders:
testBuilder:
import: "package:test_anotation/builder/test_builder.dart"
builder_factories: ["testBuilder"]
build_extensions: {".dart": [".g.part"]}
auto_apply: root_package
build_to: source
Import: the specific path of the file where the builder’s global method is located
builder_ Factories: the method name of the builder’s global method
Build run run, (parsing annotation to generate code)
After completing the above work, you can directly run the following commands in the command line window of the project to start parsing annotations and generating code:
Clean up generated files:
flutter packages pub run build_runner clean
Generate code file:
flutter packages pub run build_runner build
According to model_ Class.dart file, and generate the file model in the same directory_ class.g.dart

According to the parser interception logic, the generated code is:
// GENERATED CODE - DO NOT MODIFY BY HAND
// **************************************************************************
// TestGenerator
// **************************************************************************
class ClassModelAPT {
/**
The intercepted member fields in class are:
String* className
List<Student*>* members
Member methods intercepted in class:
void funPrint(String* content)
*/
}
Summary tips:
-
A generator can only parse one type of annotation. If there are multiple types of annotation, you need to create multiple parsers. (corresponding contents need to be added synchronously in the configuration file)
-
Using source_ The default processor provided by Gen: generatorforannotation. The processor can only handle top-level elements, such as class, function, enums, etc. defined directly in the. Dart file, but annotations used on fields and functions inside the class cannot be intercepted.