During my journey as a software engineer I have always asked myself, how can I increase my productivity, how can I build good software quickly or how can I avoid writing boilerplate code?

As programmers, we don’t like to write boilerplate code or duplicate code, it’s frustrating and boring.

For example why do you write this boilerplate, error prone, time consuming code:

When you could just write this and get the same capabilities:

Nowadays to remediate this kind of problem we use Code generation.

Example: Dagger, Retrofit, Room, Moxy and more…

So today we will learn how to use code generation in Dart to boost our productivity as a Flutter developer.

We will write a Dart code Builder that generates code based on Dart source code annotated with our annotation .

So first we need to create a dart package that will contain the annotation that we will use as the metadata required for creating a page routes.

Here we defined some information about the Dart package containing the annotation, example the name of the package, the description of the package and the dart sdk version supported.

In Dart an annotation can be any class containing a const constructor because Dart needs to known the data of the annotation at compile time.

The pubspec.yaml of the builder package is similar to the annotation one, but with two new dependencies that will allow us to generate code:

Provides utilities for an automated source code generation for Dart.

A fluent, builder-based library for generating valid Dart code.

For our use case we care only about two configurations, targets and builders.

There are a few configuration options available for a target’s builder:

enabled : Boolean, Optional: Whether to apply the builder to this target. Omit this key if you want the default behavior based on the builder’s auto_apply configuration.

generate_for : List of String or Map, Optional: The subset of files within the target’s sources which should have this Builder applied.

options : Map, Optional: A free-form map which will be passed to the Builder as a BuilderOptions when it is constructed.

dev_options : Map, Optional: A free-form map which will be passed to the Builder as a BuilderOptions when it is constructed. Usage varies depending on the particular builder.

release_options : Map, Optional: A free-form map which will be passed to the Builder as a BuilderOptions when it is constructed.

build_to configuration option sets where to output the assets.

applies_builders configuration option defines other builders that are needed to be a pplied if our builder is applied.

For our use case we will use the SharedPartBuilder as it helps to work with multiple builder outputs, so our builder won’t affect other builder used by client of our builder’s package.

The SharedPartBuilder constructor expects two required arguments and there is only one we are interested in, the list of generators and will uniquely identify the output of our builder .

Generators are classes that actually have the logic to generate code based on input provided.

So we need to create a generator that will generate code for classes with our annotation.

and update our build factory function.

So now if we test it, that generator will generate code: