Na primeira parte, descobrimos por que a geração de código é necessária e listamos as ferramentas necessárias para a geração de código no Dart. Na segunda parte, aprenderemos como criar e usar anotações no Dart, além de como usar o source_gen e o build_runner para iniciar a geração de código.

Anotações de dardo
As anotações são metadados sintáticos que podem ser adicionados ao código. Em outras palavras, é uma oportunidade de adicionar informações adicionais a qualquer componente do código, por exemplo, a uma classe ou método. As anotações são amplamente usadas no código Dart: usamos @required para indicar que um parâmetro nomeado é necessário e nosso código não será compilado se o parâmetro anotado não for especificado. Também usamos @override para indicar que uma determinada API definida em uma classe pai é implementada em uma classe filho. As anotações sempre começam com o símbolo @ .
Como criar sua própria anotação?
Embora a ideia de adicionar metadados ao código pareça um pouco exótica e complicada, as anotações são uma das coisas mais simples da linguagem Dart. Foi dito anteriormente que as anotações simplesmente trazem informações adicionais . Eles são semelhantes ao PODO (Plain Old Dart Objects). E qualquer classe pode servir como uma anotação se um construtor const estiver definido nela :
 class { final String name; final String todoUrl; const Todo(this.name, {this.todoUrl}) : assert(name != null); } @Todo('hello first annotation', todoUrl: 'https://www.google.com') class HelloAnnotations {} 
Como você pode ver, as anotações são muito simples. E o que importa é o que faremos com essas anotações. Isso nos ajudará source_gen e build_runner .
Como usar o build_runner?
build_runner é um pacote Dart que nos ajudará a gerar arquivos usando o código Dart. Vamos configurar os arquivos do Builder usando o build.yaml . Quando configurado, o Builder será chamado em todos os comandos de build ou quando o arquivo for alterado. Também temos a oportunidade de analisar o código que foi modificado ou atende a determinados critérios.
source_gen para entender o código Dart
De certa forma, o build_runner é um mecanismo que responde à pergunta " Quando preciso gerar código?" Ao mesmo tempo, o source_gen responde à pergunta " Que código deve ser gerado?". source_gen fornece uma estrutura para criar Builders para o build_runner funcionar. O source_gen também fornece uma API conveniente para analisar e gerar código.
Juntando tudo: relatório TODO
No restante deste artigo, desmontaremos o projeto todo_reporter.dart , que pode ser encontrado aqui .
Há uma regra não escrita que todos os projetos que usam geração de código seguem: você precisa criar um pacote contendo anotações e um pacote separado para o gerador que usa essas anotações. Informações sobre como criar uma biblioteca de pacotes no Dart / Flutter podem ser encontradas aqui .
Primeiro, você precisa criar o diretório todo_reporter.dart . Dentro deste diretório, você precisa criar o diretório todo_reporter , que conterá a anotação, o diretório todo_reporter_generator para processar a anotação e, finalmente, o diretório de example contém uma demonstração dos recursos da biblioteca que está sendo criada.
O sufixo .dart foi adicionado ao nome do diretório raiz para maior clareza. Obviamente, isso não é necessário, mas eu gosto de seguir esta regra para indicar com precisão o fato de que este pacote pode ser usado em qualquer projeto Dart. Pelo contrário, se eu quisesse indicar que este pacote é apenas para o Flutter (como ozzie.flutter ), usaria um sufixo diferente. Isso não é necessário, é apenas uma convenção de nomenclatura que tento aderir.
Criando todo_reporter, nosso simples pacote de anotações
Vamos criar todo_reporter dentro de todo_reporter.dart . Para fazer isso, crie o arquivo pubspec.yaml e o diretório lib .
pubspec.yaml muito simples:
 name: todo_reporter description: Keep track of all your TODOs. version: 1.0.0 author: Jorge Coca <jcocaramos@gmail.com> homepage: https://github.com/jorgecoca/todo_reporter.dart environment: sdk: ">=2.0.0 <3.0.0" dependencies: dev_dependencies: test: 1.3.4 
Não há dependências, exceto o pacote de test usado no processo de desenvolvimento.
No diretório lib , faça o seguinte:
- Você precisa criar o arquivo todo_reporter.dart, no qual, usando aexport, todas as classes que possuem uma API pública serão especificadas. Essa é uma boa prática, já que qualquer classe em nosso pacote pode ser importada usandoimport 'package:todo_reporter/todo_reporter.dart';. Você pode ver esta aula aqui .
- Dentro do diretório lib, criaremos o diretóriosrccontendo todo o código - público e não público.
No nosso caso, tudo o que precisamos adicionar é uma anotação. Vamos criar um arquivo todo.dart com nossa anotação:
 class Todo { final String name; final String todoUrl; const Todo(this.name, {this.todoUrl}) : assert(name != null); } 
Então, isso é tudo o que é preciso para anotar. Eu disse que será simples. Mas isso não é tudo. Vamos adicionar testes de unidade ao diretório de test :
todo_test.dart import 'package:test/test.dart'; import 'package:todo_reporter/todo_reporter.dart'; void main() { group('Todo annotation', () { test('must have a non-null name', () { expect(() => Todo(null), throwsA(TypeMatcher<AssertionError>())); }); test('does not need to have a todoUrl', () { final todo = Todo('name'); expect(todo.todoUrl, null); }); test('if it is a given a todoUrl, it will be part of the model', () { final givenUrl = 'http://url.com'; final todo = Todo('name', todoUrl: givenUrl); expect(todo.todoUrl, givenUrl); }); }); } 
 É tudo o que precisamos para criar a anotação. Você pode encontrar o código aqui . Agora podemos ir ao gerador.
Fazendo um trabalho interessante: todo_reporter_generator
Agora que sabemos como criar pacotes, vamos criar o pacote todo_reporter_generator . Dentro deste pacote devem estar os build.yaml e build.yaml e um diretório lib . O diretório lib deve ter o diretório src e o arquivo builder.dart . Nosso todo_reporter_generator é considerado um pacote separado que será adicionado como dev_dependency a outros projetos. Isso ocorre porque a geração de código é necessária apenas no estágio de desenvolvimento e não precisa ser adicionada ao aplicativo finalizado.
pubspec.yaml o seguinte:
 name: todo_reporter_generator description: An annotation processor for @Todo annotations. version: 1.0.0 author: Jorge Coca <jcocaramos@gmail.com> homepage: https://github.com/jorgecoca/todo_reporter.dart environment: sdk: ">=2.0.0 <3.0.0" dependencies: build: '>=0.12.0 <2.0.0' source_gen: ^0.9.0 todo_reporter: path: ../todo_reporter/ dev_dependencies: build_test: ^0.10.0 build_runner: '>=0.9.0 <0.11.0' test: ^1.0.0 
Agora vamos criar o build.yaml . Este arquivo contém a configuração necessária para nossos construtores . Mais detalhes podem ser encontrados aqui . build.yaml o seguinte:
 targets: $default: builders: todo_reporter_generator|todo_reporter: enabled: true builders: todo_reporter: target: ":todo_reporter_generator" import: "package:todo_reporter_generator/builder.dart" builder_factories: ["todoReporter"] build_extensions: {".dart": [".todo_reporter.g.part"]} auto_apply: dependents build_to: cache applies_builders: ["source_gen|combining_builder"] 
A propriedade import aponte para o arquivo que contém o Builder e a propriedade builder_factories aponte para os métodos que irão gerar o código.
Agora podemos criar o arquivo builder.dart no diretório lib :
 import 'package:build/build.dart'; import 'package:source_gen/source_gen.dart'; import 'package:todo_reporter_generator/src/todo_reporter_generator.dart'; Builder todoReporter(BuilderOptions options) => SharedPartBuilder([TodoReporterGenerator()], 'todo_reporter'); 
E o arquivo todo_reporter_generator.dart no diretório src :
 import 'dart:async'; import 'package:analyzer/dart/element/element.dart'; import 'package:build/src/builder/build_step.dart'; import 'package:source_gen/source_gen.dart'; import 'package:todo_reporter/todo_reporter.dart'; class TodoReporterGenerator extends GeneratorForAnnotation<Todo> { @override FutureOr<String> generateForAnnotatedElement( Element element, ConstantReader annotation, BuildStep buildStep) { return "// Hey! Annotation found!"; } } 
Como você pode ver, no arquivo todoReporter , definimos o método todoReporter que o Builder cria. Builder é criado usando o SharedPartBuilder , que usa o TodoReporterGenerator . Então build_runner e source_gen trabalham juntos.
Nosso TodoReporterGenerator é uma subclasse de GeneratorForAnnotation , portanto, o método generateForAnnotatedElement será executado apenas quando esta anotação ( @Todo no nosso caso) for encontrada no código.
O método generateForAnnotatedElement retorna uma sequência que contém nosso código gerado. Se o código gerado não for compilado, toda a fase de construção falhará . Isso é muito útil, pois evita erros no futuro.
Assim, a cada geração de código, nosso todo_repoter_generator criará um arquivo de part , com o comentário // Hey! Annotation found! // Hey! Annotation found! No próximo artigo, aprenderemos como processar anotações.
Juntando tudo: usando todo_reporter
Agora você pode demonstrar como todo_reporter.dart . É uma boa prática adicionar um projeto de example ao trabalhar com pacotes. Portanto, outros desenvolvedores poderão ver como a API pode ser usada em um projeto real.
Vamos criar um projeto e adicionar as dependências necessárias ao pubspec.yaml . No nosso caso, criaremos um projeto Flutter dentro do diretório de example e adicionaremos as dependências:
 dependencies: flutter: sdk: flutter todo_reporter: path: ../todo_reporter/ dev_dependencies: build_runner: 1.0.0 flutter_test: sdk: flutter todo_reporter_generator: path: ../todo_reporter_generator/ 
Depois de receber os pacotes (pacotes flutter packages get ), podemos usar nossa anotação:
 import 'package:todo_reporter/todo_reporter.dart'; @Todo('Complete implementation of TestClass') class TestClass {} 
Agora que tudo está no lugar, execute nosso gerador:
 $ flutter packages pub run build_runner build 
Após a conclusão do comando, você notará um novo arquivo em nosso projeto: todo.g.dart . Ele conterá o seguinte:
 
Conseguimos o que queríamos! Agora podemos gerar o arquivo Dart correto para cada anotação @Todo em nosso código. Experimente e crie quantas você precisar.
No próximo artigo
Agora, temos as configurações corretas para gerar arquivos. No próximo artigo, aprenderemos como usar anotações para que o código gerado possa fazer coisas realmente interessantes. Afinal, o código que está sendo gerado agora não faz muito sentido.