Flutter編譯時生成程式碼之 code_builder

陳平大將發表於2021-03-29

前言

之前學習原生開發的時候使用過各種編譯自動生成模板程式碼的框架,例如ARouter,這些框架其實是藉助了JavaPoet 這個框架來自動生成程式碼的,JavaPoet 可以在編譯自動生成模板程式碼,在flutter,也有這樣的框架可以在編譯時自動生成程式碼,這個框架就是 code_builder

程式碼

code_builder內部提供了一系列的api給開發人員使用,憑藉這些api,我們可以建立任何程式碼。假如我們現在需要建立一個User類,程式碼如下:

class User {
  User.User([this._name, this._age, this._sex]);

  String _name;

  int _age;

  int _sex;

  String get name => _name;
  int get age => _age;
  get sex => _sex;
  set name(String value) {
    _name = value;
  }

  set age(int value) {
    _age = value;
  }

  set sex(int value) {
    _sex = value;
  }

  @override
  String toString() {
    return "name = $_name; sex = $_sex; age = $_age";
  }
}
複製程式碼

那我們該如何呼叫code_builder提供的api去建立這樣一個類呢?可以這麼做,如下程式碼所示:

void getUser() {
  final user = Class((classBuild) => classBuild//生成一個類
    ..name = "User"//這個類的名字叫User
    ..fields.add(Field((fieldBuild) => fieldBuild//新增成員變數
      ..name = "_name"//該變數為私有變數,變數名為_name
      ..type = refer("String")//變數型別為String
    ))
    ..fields.add(Field((fieldBuild) => fieldBuild
      ..name = "_age"
      ..type = refer("int")
    ))
    ..fields.add(Field((fieldBuild) => fieldBuild
      ..name = "_sex"
      ..type = refer("int")
    ))
    ..constructors.add(Constructor((constructorBuilder){//新增建構函式
      constructorBuilder.name = "User";//建構函式的名字為User
      constructorBuilder.optionalParameters.add(Parameter((parameterBuilder) {//給建構函式新增一個引數
        parameterBuilder.name = "_name";// 給建構函式新增一個引數_name
        parameterBuilder.toThis = true;// 將引數_name賦值給成員變數_name
       }));
      constructorBuilder.optionalParameters.add(Parameter((parameterBuilder) {
        parameterBuilder.name = "_age";
        parameterBuilder.toThis = true;
      }));
      constructorBuilder.optionalParameters.add(Parameter((parameterBuilder) {
        parameterBuilder.name = "_sex";
        parameterBuilder.toThis = true;
      }));
    }))
    ..methods.add(Method((methodBuild) {// 建立一個方法
      methodBuild.type = MethodType.getter;// 方法是get型別的方法
      methodBuild.name = "name";// 方法名是name
      methodBuild.returns = refer("String");//方法返回型別是String
      methodBuild.lambda = true;// 方法是一個lambda表示式
      methodBuild.body = const Code("_name");// 建立方法體內的程式碼
    }))
    ..methods.add(Method((methodBuild){
      methodBuild.type = MethodType.getter;
      methodBuild.name = "age";
      methodBuild.returns = refer("int");
      methodBuild.lambda = true;
      methodBuild.body = const Code("_age");
    }))
    ..methods.add(Method((methodBuild){
      methodBuild.type = MethodType.getter;
      methodBuild.name = "sex";
      methodBuild.lambda = true;
      methodBuild.body = const Code("_sex");
    }))
    ..methods.add(Method((methodBuild) { //建立一個方法
      methodBuild.type = MethodType.setter; // 方法是set型別的方法
      methodBuild.name = "name";  // 方法名是name
      methodBuild.lambda = false; // 不是lambda表示式
      methodBuild.requiredParameters.add(Parameter((parameterBuild) { // 給方法新增引數
        parameterBuild.name = "value"; // 引數名是value
        parameterBuild.type = refer("String"); // 引數類似是String
      }));
      methodBuild.body = const Code("_name = value;"); // 方法體,對_name賦值為value
    }))
    ..methods.add(Method((methodBuild){
      methodBuild.type = MethodType.setter;
      methodBuild.name = "age";
      methodBuild.lambda = false;
      methodBuild.requiredParameters.add(Parameter((parameterBuild){
        parameterBuild.name = "value";
        parameterBuild.type = refer("int");
      }));
      methodBuild.body = const Code("_age = value;");
    }))
    ..methods.add(Method((methodBuild){
      methodBuild.type = MethodType.setter;
      methodBuild.name = "sex";
      methodBuild.lambda = false;
      methodBuild.requiredParameters.add(Parameter((parameterBuild){
        parameterBuild.name = "value";
        parameterBuild.type = refer("int");
      }));
      methodBuild.body = const Code("_sex = value;");
    }))
    ..methods.add(Method((methodBuild) {
      methodBuild.name = "toString";
      methodBuild.lambda = false;
      methodBuild.returns = refer("String");
      methodBuild.annotations.add(TypeReference((build) {// 給方法新增註解
        build.symbol = "override";//註解型別是@override,表示這是一個重寫的方法
      }));
      methodBuild.body = const Code("return \"name = \$_name; sex = \$_sex; age = \$_age\"; ");
    }))
  );
  final emitter = DartEmitter();
  print(DartFormatter().format('${user.accept(emitter)}'));
}

void main() {
  getUser();
}
複製程式碼

當然,光靠code_builder是不足以生成可以在實際專案中可以使用的類的,因為它只是建立了程式碼模板而已,我們還需要將這些程式碼模板寫入到工程目錄的某一個dart檔案中,這個就需要藉助source_gen了,source_gen可以在編譯時解析註解,然後藉助code_builder生成程式碼模板,最終將這些程式碼模板寫入到工程目錄的某一個dart檔案中。

QQ交流群

群號碼:414108143

相關文章