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 a export
, 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 usando import 'package:todo_reporter/todo_reporter.dart';
. Você pode ver esta aula aqui . - Dentro do diretório
lib
, criaremos o diretório src
contendo 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.