'How to read the Annotation fields and their values (Code Generation)

i have been struggling to get the fields from an annotation.

I am using the source code generator to generate my widget from my annotation but cant seem to use the annotation fields, how can i access the fields and the values.

My Annotation:

class CoreEntity {
  final String label;

  const CoreEntity({
    this.label,
  });
}

Using the annotation:

@CoreEntity(
  label: 'User',
)
class User {
  String email;
}

My Widget Generator:

class CoreWidgetGenerator extends GeneratorForAnnotation<CoreEntity> {
  @override
  FutureOr<String> generateForAnnotatedElement(
      Element element, ConstantReader annotation, BuildStep buildStep) {
    return _generateWidgetSource(element);
  }

  String _generateWidgetSource(Element element) {
    final anotData = element.metadata;
    for (ElementAnnotation el in anotData) {
      el.computeConstantValue();
      var elVisitor = ModelVisitor();
      el.element.visitChildren(elVisitor);
      print("1 ${elVisitor.className}"); // this prints out null
      print("2 ${elVisitor.fields}"); // this prints out empty list {}
    }
}

class ModelVisitor extends SimpleElementVisitor {
  DartType className;
  Map<String, DartType> fields = Map();

  @override
  visitConstructorElement(ConstructorElement element) {
    className = element.type.returnType;
    return super.visitConstructorElement(element);
  }

  @override
  visitFieldElement(FieldElement element) {
    fields[element.name] = element.type;

    return super.visitFieldElement(element);
  }
}

Packages in pubspec.yaml

dependencies:
  build: ^1.1.6
  source_gen: ^0.9.4+4 
  core_widget:
    path: ../core_widgets

Build Script (build.yaml)

targets:
  $default:
    builders:
      core_gen|core_widget:
        enabled: true

builders:
  core_widget:
      target: ":core_gen"
      import: "package:core_gen/builder.dart"
      builder_factories: ["coreWidget"]
      build_extensions: {".dart":[".core_widget.g.part"]}
      auto_apply: dependents
      build_to: cache
      applies_builders: ["source_gen|combining_builder"]

References|Research i have done but with no luck.



Solution 1:[1]

You already have access to the annotation properties via the ConstantReader

class CoreWidgetGenerator extends GeneratorForAnnotation<CoreEntity> {
  @override
  FutureOr<String> generateForAnnotatedElement(
      Element element, ConstantReader annotation, BuildStep buildStep) {
    return _generateWidgetSource(element, annotation);
  }

  String _generateWidgetSource(Element element, ConstantReader annotation) {
    print(annotation.read('label').stringValue);  
  }
}

However if you want to retrieve annotation information for a field of a ClassElement you can use a TypeChecker

Annotation

class CoreProperty {
  final String label;

  const CoreProperty({ this.label});
}

Usage

@CoreEntity(label: 'User')
class User {

  @CoreProperty(label: 'Email')
  String email;
}
final _coreChecker = const TypeChecker.fromRuntime(CoreProperty);

class CoreWidgetGenerator extends GeneratorForAnnotation<CoreEntity> {
  @override
  FutureOr<String> generateForAnnotatedElement(
      Element element, ConstantReader annotation, BuildStep buildStep) {
    return _generateWidgetSource(element as ClassElement, annotation);
  }

  String _generateWidgetSource(ClassElement element, ConstantReader annotation) {

    for (var f in element.fields) {
      if (_coreChecker.hasAnnotationOfExact(f)) {
        print(_coreChecker
          .firstAnnotationOfExact(f)
          .getField('label')
          .toStringValue()
        );
      }
    }
  }
}

Solution 2:[2]

Based on Dan's answer, to get the value of an enum field:

final _coreChecker = const TypeChecker.fromRuntime(CoreProperty);

class CoreWidgetGenerator extends GeneratorForAnnotation<CoreEntity> {
  @override
  FutureOr<String> generateForAnnotatedElement(
      Element element, ConstantReader annotation, BuildStep buildStep) {
    return _generateWidgetSource(element as ClassElement, annotation);
  }

  String _generateWidgetSource(ClassElement element, ConstantReader annotation) {

    for (var f in element.fields) {
      if (_coreChecker.hasAnnotationOfExact(f)) {
        print(_coreChecker
          .firstAnnotationOfExact(f)
          .getField('label')
          .toStringValue()
          
          // for enum field named myEnum with an Enum type called MyEnumType
          int? myEnumIndex = _coreChecker.firstAnnotationOfExact(f)?.getField('myEnum')?.getField("index")?.toIntValue();
        MyEnumType myEnumValue = MyEnumType.values[myEnumIndex!];
        print(myEnumValue)
          
        );
      }
    }
  }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Dan
Solution 2 Ethan