Dans la première partie, nous avons découvert pourquoi la génération de code est nécessaire et répertorié les outils nécessaires pour la génération de code dans Dart. Dans la deuxième partie, nous apprendrons comment créer et utiliser des annotations dans Dart, ainsi que comment utiliser source_gen et build_runner pour démarrer la génération de code.

Annotations de fléchettes
Les annotations sont des métadonnées syntaxiques qui peuvent être ajoutées au code. En d'autres termes, c'est l'occasion d'ajouter des informations supplémentaires à n'importe quel composant du code, par exemple, à une classe ou une méthode. Les annotations sont largement utilisées dans le code Dart: nous utilisons @required
pour indiquer qu'un paramètre nommé est requis, et notre code ne se compilera pas si le paramètre annoté n'est pas spécifié. Nous utilisons également @override
pour indiquer qu'une API donnée définie dans une classe parent est implémentée dans une classe enfant. Les annotations commencent toujours par le symbole @
.
Comment créer votre propre annotation?
Bien que l'idée d'ajouter des métadonnées au code semble un peu exotique et compliquée, les annotations sont l'une des choses les plus simples du langage Dart. Il a été dit précédemment que les annotations contiennent simplement des informations supplémentaires . Ils sont similaires aux PODO (Plain Old Dart Objects). Et n'importe quelle classe peut servir d'annotation si un constructeur const
est défini :
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 {}
Comme vous pouvez le voir, les annotations sont très simples. Et ce qui compte, c'est ce que nous ferons de ces annotations. Cela nous aidera à source_gen
et build_runner
.
Comment utiliser build_runner?
build_runner
est un package Dart qui nous aidera à générer des fichiers à l'aide du code Dart. Nous allons configurer les fichiers Builder
en utilisant build.yaml
. Lorsqu'il est configuré, Builder
sera appelé à chaque commande de build
ou lorsque le fichier est modifié. Nous avons également la possibilité d'analyser du code qui a été modifié ou qui répond à certains critères.
source_gen pour comprendre le code Dart
Dans un sens, build_runner
est un mécanisme qui répond à la question " Quand dois-je générer du code?" Dans le même temps, source_gen
répond à la question « Quel code doit être généré?». source_gen
fournit un cadre pour créer des build_runner
pour que build_runner
fonctionne. source_gen
fournit également une API pratique pour analyser et générer du code.
Tout mettre ensemble: rapport TODO
Dans la suite de cet article, nous démonterons le projet todo_reporter.dart , qui se trouve ici .
Il existe une règle non écrite que tous les projets qui utilisent la génération de code suivent: vous devez créer un package contenant des annotations et un package distinct pour le générateur qui utilise ces annotations. Vous trouverez ici des informations sur la création d'une bibliothèque de packages dans Dart / Flutter.
Vous devez d'abord créer le répertoire todo_reporter.dart
. Dans ce répertoire, vous devez créer le répertoire todo_reporter
, qui contiendra l'annotation, le répertoire todo_reporter_generator
pour traiter l'annotation et, enfin, l' example
répertoire contenant une démonstration des capacités de la bibliothèque en cours de création.
Le suffixe .dart
été ajouté au nom du répertoire racine pour plus de clarté. Bien sûr, ce n'est pas nécessaire, mais j'aime suivre cette règle pour indiquer avec précision le fait que ce package peut être utilisé dans n'importe quel projet Dart. Au contraire, si je voulais indiquer que ce package est uniquement pour Flutter (comme ozzie.flutter ), j'utiliserais un suffixe différent. Ce n'est pas nécessaire, c'est juste une convention de dénomination à laquelle j'essaie d'adhérer.
Création de todo_reporter, notre package d'annotation simple
Nous allons créer todo_reporter
dans todo_reporter.dart
. Pour ce faire, créez le fichier pubspec.yaml
et le répertoire lib
.
pubspec.yaml
très simple:
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
Il n'y a pas de dépendances à l'exception du package de test
utilisé dans le processus de développement.
Dans le répertoire lib
, procédez comme suit:
- Vous devez créer un fichier
todo_reporter.dart
dans lequel, à l'aide de l' export
, toutes les classes qui ont une API publique seront spécifiées. C'est une bonne pratique, car n'importe quelle classe de notre package peut être importée en utilisant import 'package:todo_reporter/todo_reporter.dart';
. Vous pouvez voir cette classe ici . - À l'intérieur du répertoire
lib
, nous allons créer le répertoire src
contenant tout le code - public et non public.
Dans notre cas, il suffit d'ajouter une annotation. Créons un fichier todo.dart
avec notre annotation:
class Todo { final String name; final String todoUrl; const Todo(this.name, {this.todoUrl}) : assert(name != null); }
C'est tout ce qu'il faut pour annoter. J'ai dit que ce sera simple. Mais ce n'est pas tout. Ajoutons des tests unitaires au répertoire 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); }); }); }
C'est tout ce dont nous avons besoin pour créer l'annotation. Vous pouvez trouver le code ici . Maintenant, nous pouvons aller au générateur.
Faire un travail sympa: todo_reporter_generator
Maintenant que nous savons comment créer des packages, créons le package todo_reporter_generator
. A l'intérieur de ce paquet devrait se build.yaml
pubspec.yaml
et build.yaml
et un répertoire lib
. Le répertoire lib
doit avoir le répertoire src
et le fichier builder.dart
. Notre todo_reporter_generator
est considéré comme un package séparé qui sera ajouté en tant que dev_dependency
à d'autres projets. En effet, la génération de code n'est nécessaire qu'au stade du développement et n'a pas besoin d'être ajoutée à l'application terminée.
pubspec.yaml
le suivant:
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
build.yaml
maintenant build.yaml
. Ce fichier contient la configuration nécessaire à nos constructeurs . Plus de détails peuvent être trouvés ici . build.yaml
le suivant:
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"]
La propriété import
pointe vers le fichier qui contient le builder_factories
et la propriété builder_factories
pointe vers les méthodes qui généreront le code.
Nous pouvons maintenant créer le fichier builder.dart
dans le répertoire 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');
Et le fichier todo_reporter_generator.dart
dans le répertoire 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!"; } }
Comme vous pouvez le voir, dans le fichier builder.dart
, nous avons défini la méthode todoReporter
le todoReporter
. Builder
SharedPartBuilder
est créé à l'aide de SharedPartBuilder
, qui utilise notre TodoReporterGenerator
. Donc build_runner
et source_gen
fonctionnent ensemble.
Notre TodoReporterGenerator
est une sous-classe de GeneratorForAnnotation
, donc la méthode generateForAnnotatedElement
ne sera exécutée que lorsque cette annotation ( @Todo
dans notre cas) se trouve dans le code.
La méthode generateForAnnotatedElement
renvoie une chaîne contenant notre code généré. Si le code généré ne se compile pas, la phase de génération entière échouera . Ceci est très utile car il évite les erreurs à l'avenir.
Ainsi, à chaque génération de code, notre todo_repoter_generator
créera un fichier part
, avec commentaire // Hey! Annotation found!
// Hey! Annotation found!
Dans le prochain article, nous apprendrons comment traiter les annotations.
Mettre tout cela ensemble: en utilisant todo_reporter
Vous pouvez maintenant montrer comment todo_reporter.dart
. Il est recommandé d'ajouter un example
projet lorsque vous travaillez avec des packages. Ainsi, d'autres développeurs pourront voir comment l'API peut être utilisée dans un vrai projet.
Créons un projet et ajoutons les dépendances requises à pubspec.yaml
. Dans notre cas, nous allons créer un projet Flutter dans le répertoire d' example
et ajouter les dépendances:
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/
Après avoir reçu les paquets (les paquets flutter packages get
), nous pouvons utiliser notre annotation:
import 'package:todo_reporter/todo_reporter.dart'; @Todo('Complete implementation of TestClass') class TestClass {}
Maintenant que tout est en place, lancez notre générateur:
$ flutter packages pub run build_runner build
Une fois la commande terminée, vous remarquerez un nouveau fichier dans notre projet: todo.g.dart
. Il contiendra les éléments suivants:
Nous avons réalisé ce que nous voulions! Nous pouvons maintenant générer le fichier Dart correct pour chaque annotation @Todo
dans notre code. Essayez d'en créer autant que vous en avez besoin.
Dans le prochain article
Nous avons maintenant les bons paramètres pour générer des fichiers. Dans le prochain article, nous apprendrons à utiliser les annotations pour que le code généré puisse faire des choses vraiment cool. Après tout, le code généré actuellement n'a pas beaucoup de sens.