[Flutter]コード生成したい!その1
json_serializableやfreezedを使うと便利なコードを自動生成してくれます。
この生成ってどうやっているのか、気になったのでまとめて記事にしてみます。
とりあえずなにか生成したい
次のようなコードを書いたら、test.g.dartファイルが生成したい。
part 'test.g.dart';
()
class A {}
準備
ディレクトリ構造は
dart_code_gen
|
+-- hello_code_gen
+-- example
という構造にしました。
hello_code_genの方にコード生成のためのコードを書いて、exampleで動作確認する感じです。
hello_code_genはflutter create --template=package hello_code_gen
で生成しました。
exampleはflutter create example
で生成しました。
hello_code_genのpubspec.yaml
ライブラリはsource_gen, analyzer, build, build_runnerを使います。
...略
dependencies:
flutter:
sdk: flutter
analyzer:
build:
source_gen:
dev_dependencies:
flutter_test:
sdk: flutter
build_runner:
...略
アノテーションの定義とコード生成
hello_code_gen.dartにアノテーション自体の定義とアノテーションされた要素に対して、生成を行うコードを書きます。
library hello_code_gen;
import 'package:analyzer/dart/element/element.dart';
import 'package:build/build.dart';
import 'package:source_gen/source_gen.dart';
class HelloAnno {
const HelloAnno();
}
class HelloAnnoGenerator extends GeneratorForAnnotation<HelloAnno> {
generateForAnnotatedElement(
Element element,
ConstantReader annotation,
BuildStep buildStep,
) {
return "class Hello {}";
}
}
GeneratorForAnnotationのジェネリクスとしてHelloAnnoを渡すことで、@HelloAnno
のようにアノテーションされている要素に対して、コード生成を行うように設定できます。
コードの生成はgenerateForAnnotatedElementメソッドを使用します。引数としてアノテーションされたクラスの情報が渡されるようなのですが、今回は使用しません。返り値としてStringを返すことで、そのStringがコードして生成されます。
今回はどんなクラスにアノテーションされたかに関係なく、class Hello {}
というコードを生成します。
builderの定義
build_runnerに登録するために, Builderクラスを返す関数を定義する必要があります。
source_genパッケージではコードの生成タイプに合わせて3種類のBuilderが用意されていますが、今回はSharedPartBuilderを使用します。source_genのREADME
import 'package:build/build.dart';
import 'package:source_gen/source_gen.dart';
import 'hello_code_gen.dart';
Builder helloBuilder(BuilderOptions _) =>
SharedPartBuilder([HelloAnnoGenerator()], "hello");
build.yamlの設定
build_runnerで生成をしてもらうためにbuild.yamlを記述する必要があります。
builders:
hello_code_gen:
import: "package:hello_code_gen/builder.dart"
builder_factories: ["helloBuilder"]
build_extensions: {".dart": [".g.dart"]}
auto_apply: dependents
applies_builders: ["source_gen|combining_builder"]
importで本パッケージのbuilderを返す関数があるファイルを指定して、 builder_factoriesで関数名を指定しました。
その他のプロパティの詳しい仕様はここに書いてあります。
使ってみる
exampleの方のプロジェクトに移動して、コード生成をしてみます。
その前にpubspec.yamlを開いて、hello_code_genとbuild_runnerを設定します。
dependencies:
flutter:
sdk: flutter
hello_code_gen:
path: ../hello_code_gen
cupertino_icons: ^1.0.2
dev_dependencies:
flutter_test:
sdk: flutter
build_runner:
test.dartなど適当なファイルに次のコードを書きます。
import 'package:hello_code_gen/hello_code_gen.dart';
part 'test.g.dart';
()
class A {}
ターミナルを開いてflutter pub run build_runner build --delete-conflicting-outputs
します。
コード生成が完了すると、test.g.dartに次のコードが生成されるはずです。
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'test.dart';
// **************************************************************************
// HelloAnnoGenerator
// **************************************************************************
class Hello {}
まとめ
とりあえず生成できました。
次は, generateForAnnotatedElementメソッドの引数で情報を受け取って、なにかしら生成を変更するようにしてみたいです。
Discussion