Home Flutter中使用metadata生成代码
Post
Cancel

Flutter中使用metadata生成代码

Dart 中的 metadata ,就是Java 中的注解。我们知道,利用注解可以在Java编译期生成代码,那Dart的metadata也能够生成代码吗?

答案是肯定的。在Flutter处理Json时就是利用了注解JsonSerializable帮助我们生成了一部分代码,关于Json的基础操作可以参考 这一篇.


接下来,要如何生成代码?

  1. 添加依赖。

    1
    2
    3
    
    dev_dependencies:
      build_runner: ^1.0.0
      source_gen: ^0.9.0
    
  2. 创建一个注解。

    1
    2
    3
    4
    5
    
    class TestMetadata {
         
      const TestMetadata();
         
    }
    
  3. 创建GeneratorForAnnotation子类。该类就是用来生成具体内容的。

    1
    2
    3
    4
    5
    6
    7
    8
    
    class TestGenerator extends GeneratorForAnnotation<TestMetadata> {
      @override
      generateForAnnotatedElement(Element element, ConstantReader annotation, BuildStep buildStep) {
          
        return "//TestMetadata generate this";
      }
         
    }
    

    Element就是注解标注的那个元素,可以是类、变量、方法等。

  4. 创建一个方法体,并返回一个Builder

    1
    2
    
    Builder testBuilder(BuilderOptions options) => 
        SharedPartBuilder([TestGenerator()], "test_metadata");
    

    SharedPartBuilder 的第一个入参是生成器的集合, 第二个入参可以理解为builder的一个别名。

  5. 在根目录下创建一个文件: build.yaml。关于这个配置文件的详细说明,参考这一篇

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
    targets:
      $default:
        builders:
          demo2|test_metadata:
            enabled: true
       
    builders:
      test_metadata:
        import: "package:demo2/test.dart"
        builder_factories: ["testBuilder"]
        build_extensions: {".dart": ["test_metadata.g.part"]}
        auto_apply: dependents
        build_to: cache
        applies_builders: ["source_gen|combining_builder"]
    
  6. 创建一个测试类,并添加注解。

    1
    2
    3
    4
    5
    6
    7
    8
    
    import 'package:demo2/test.dart';
       
    part 'test2.g.dart';
       
    @TestMetadata()
    class TestModel {
       
    }
    
  7. 在项目根目录运行命令:

    1
    
    flutter packages pub run build_runner build
    
  8. 一切顺利的话,会在同目录下生成test2.g.dart文件。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    // GENERATED CODE - DO NOT MODIFY BY HAND
       
    part of 'test2.dart';
       
    // **************************************************************************
    // TestGenerator
    // **************************************************************************
       
    //TestMetadata generate this
    


扩展一下,在第3步中,创建了一个继承自GeneratorForAnnotation 的子类 TestGenerator。这里可能会有一个小问题:当有多个地方被注解,在执行代码生成时,彼此是孤立的,不知道对方的存在。无法生成全局性的代码。

此时,可以直接继承Generator,可以对多个Element做集中处理。GeneratorForAnnotation 就是继承自Generator,贴一下它的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
abstract class GeneratorForAnnotation<T> extends Generator {
  const GeneratorForAnnotation();

  TypeChecker get typeChecker => TypeChecker.fromRuntime(T);

  @override
  FutureOr<String> generate(LibraryReader library, BuildStep buildStep) async {
    var values = Set<String>();

    for (var annotatedElement in library.annotatedWith(typeChecker)) {
      var generatedValue = generateForAnnotatedElement(
          annotatedElement.element, annotatedElement.annotation, buildStep);
      await for (var value in normalizeGeneratorOutput(generatedValue)) {
        assert(value == null || (value.length == value.trim().length));
        values.add(value);
      }
    }

    return values.join('\n\n');
  }

  generateForAnnotatedElement(
      Element element, ConstantReader annotation, BuildStep buildStep);
}

generate方法中,通过library.annotatedWith(typeChecker)可以拿到所有特定注解的元素


Demo地址: demo

This post is licensed under CC BY 4.0 by the author.