Code generation allows you to speed up and make the development more convenient by generating ready-made pieces of a code.
This can be used to preset the architecture of an application, for example. If you want to use the wedge architecture pattern, then you will have to spend a lot of time creating all the folders and files.
Template generation will save you this time and enable you to jump right into the development with a quick start.
Also, template generation helps not only with the architecture setup, but also speeds up the development itself.
For example, we used template generation to automatically create repositories to save time and make developers create unit tests for their code, since our template, together with the repository, creates a template with tests for this repository in the test directory.
There are a large number of approaches to generating templates in Flutter, we chose Mason package, because it is the most popular, constantly developing and improving package, which also has its own hub containing bricks (templates) that can be used with this package.
Mason is a Flutter code generation tool that helps developers create reusable code blocks, called “bricks”, for their Flutter projects.
These bricks can contain any type of code, from UI elements to business logic, and can be easily imported into any Flutter project using the Mason command line interface (CLI).
One of the main benefits of using Mason is that it allows developers to easily share their code with others, making it easier for teams to collaborate and reuse the code.
Additionally, Mason makes it easy to create and manage code templates, which can save developers a lot of time and effort when starting new projects or adding new features to existing ones.
Using Mason and the Brickhub can provide a number of benefits for Flutter developers, including:
Overall, Mason and the Brickhub provide a powerful tool for Flutter developers looking to save time and improve their workflow. Whether you’re a seasoned developer or just starting out, Mason can help you create high-quality, reusable code for your Flutter projects.
Official documentation recommend installing mason_cli from pub.dev
# 🎯 Activate from https://pub.dev
dart pub global activate mason_cli
Alternatively, mason_cli can be installed via homebrew
# 🍺 Install from https://brew.sh
brew tap felangel/mason
brew install mason
Once you have successfully installed the mason_cli you can verify your setup by running the mason command in your terminal. If mason_cli was installed correctly, you should see something similar to the following output:
🧱 mason • lay the foundation!
Usage: mason <command> [arguments]
Global options:
-h, --help Print this usage information.
--version Print the current version.
Available commands: ...
We will describe how to create your own break using as example our simple_repository brick.
mason new simple_repositoty
It will create a new, custom brick template in the current working directory.
It should generate such file structure:
├── CHANGELOG.md
├── LICENSE
├── README.md
├── __brick__
└── brick.yaml
The brick.yaml
file is a manifest which contains metadata for the current brick. The newly generated brick.yaml should look something like:
name: simple_repository
description: Brick for the simple creating repositories and tests for them.
repository: https://github.com/TBR-Group-software/simple_bricks/tree/main/bricks/simple_repository
version: 0.1.0+2
environment:
mason: ">=0.1.0-dev.26 <0.1.0"
vars:
repository_name:
type: string
description: Repository Name
default: Dash
prompt: What is your repository name?
The __brick__
directory contains the template for your brick. Any files, directories within the __brick__ will be generated when the brick is used via mason make.
In the example brick, our __brick__ directory contains a such files structure:
├── __brick__
│ ├── lib
│ │ ├── {{repository_name.snakeCase()}}
│ │ │ ├── {{repository_name.snakeCase()}}_repository
│ │ │ │ └──{{repository_name.snakeCase()}}
_repository.dart
│ │ │ └──api_{{repository_name.snakeCase()}}
_repository
│ │ │ └── api_{{repository_name.snakeCase()}}
_repository
│ │ └── {{repository_name.snakeCase()}}.dart
│ ├── test
│ │ └── repositories
│ │ └── {{repository_name.snakeCase()}}
_repository_test.dart
└── └──
Templates currently support only Mustache syntax, we will create our template using it.
Mason supports a handful of built-in lambdas that can help with customizing generated code:
Name | Example | Shorthand Syntax | Full Syntax |
---|---|---|---|
camelCase | helloWorld | {{variable.camel Case()}} | {{#camelCase}}{{varia ble}}{{/camelCase}} |
constantCase | HELLO_WORLD | {{variable.constant Case()}} | {{#constantCase}}{{vari able}}{{/constantCase}} |
dotCase | hello.world | {{variable.dot Case()}} | {{#dotCase}}{{variable}} {{/dotCase}} |
headerCase | Hello-World | {{variable.header Case()}} | {{#headerCase}}{{vari able}} {{/headerCase}} |
lowerCase | hello world | {{variable.header Case()}} | {{#lowerCase}}{{variable}} {{/lowerCase}} |
mustacheCase | {{ Hello World }} | {{variable.mustache Case()}} | {{#mustacheCase}}{{variable}}{{/mustacheCase}} |
pascalCase | HelloWorld | {{variable.pascal Case()}} | {{#pascalCase}}{{variable}} {{/pascalCase}} |
paramCase | hello-world | {{variable.param Case()}} | {{#paramCase}} {{variable}}{{/param Case}} |
pathCase | hello/world | {{variable.pathCase() }} | {{#pathCase}} {{variable}}{{/path Case}} |
sentenceCase | Hello world | {{variable.sentence Case()}} | {{#sentenceCase}}{{variable}}{{/sentenceCase}} |
snakeCase | hello_world | {{variable.snake Case()}} | {{#snakeCase}} {{variable}} {{/snakeCase}} |
titleCase | Hello World | {{variable.titleCase()}} | {{#titleCase}} {{variable}}{{/titleCase}} |
upper Case | HELLO WORLD | {{variable.upper Case()}} | {{#upperCase}} {{variable}}{{/upperCase}} |
{{repository_name.snakeCase()}}_repository.dart
file:
abstract class {{repository_name.pascalCase()}}Repository {}
{{repository_name.snakeCase()}}_repository.dart
file:
/// {@template api_{{repository_name.snakeCase()}}repository} /// [{{repository_name.pascalCase()}}Repository] /// {@endtemplate}
class Api{{repository_name.pascalCase()}}Repository extends {{repository_name.pascalCase()}}Repository { /// {@macro api{{repository_name.snakeCase()}}_repository}
}
{{repository_name.snakeCase()}}.dart
file:
export
'api_{{repository_name.snakeCase()}}_repository/api_{{repository_name.snakeCase()}}_repository.dart';
export
'{{repository_name.snakeCase()}}_repository/{{repository_name.snakeCase()}}_repository.dart';
{{repository_name.snakeCase()}}_repository_test.dart
file:
import 'package:flutter_test/flutter_test.dart';
void main() {
group("Testing {{repository_name.pascalCase()}} Repository", () {
//TODO: implement the tests
setUp(() {});
test("Test method", () {});
});
}
To publish a brick to the Brickhub using Mason, you will first need to sign up for an account on the Brickhub website.
To do this, you will need to request access to the site, and then follow the instructions in the email invite to sign up and verify your email.
Once you have an account, you can log in to the Brickhub using the mason login command.
This will prompt you to enter your email and password, and once you have successfully logged in, you will be ready to publish your brick. To publish a brick, you will use the mason publish command, followed by the –directory option and the path to your brick’s directory.
For example:
mason publish --directory ./my_brick
This will start the process of publishing your brick to the Brickhub.
You will be prompted to confirm that you want to publish the brick, and once you confirm, the brick will be bundled and published to the Brickhub.
It’s important to note that you will need to be logged in to an account in order to publish a brick to the Brickhub.
Overall, publishing a brick to the Brickhub is a straightforward process that can be accomplished quickly and easily using the Mason CLI.
By making your bricks available on the Brickhub, you can share them with other Flutter developers and save time and effort when working on your projects.
Mason supports Custom Script Execution so called hooks.
These hooks are defined in the application’s configuration and can be executed before or after certain events occur, such as the generation of code or the rendering of templates.
Currently, Mason only supports hooks written in the Dart programming language.
These hooks are defined in the hooks directory at the root of the brick and must contain a run method that accepts a HookContext from the package:mason/mason.dart library
There are two types of hooks available in Mason: pre_gen
and post_gen
, which are executed immediately before and after the generation step, respectively.
These hooks can be used to perform tasks such as logging, modifying the brick variables, or interacting with the logger.
For example, hooks for running dart formatting.
Future<void> _runDartFormat(HookContext context) async {
final formatProgress =
context.logger.progress('Running "dart format ."');
await Process.run('dart', ['format', '.']);
formatProgress.complete();
}
Future<void> _runDartFix(HookContext context) async {
final formatProgress =
context.logger.progress('Running "dart fix --apply"');
await Process.run('dart', ['fix', '--apply']);
formatProgress.complete();
}
Using Mason templates has provided a number of benefits for our development process, including:
Overall, using Mason templates has helped us to improve the efficiency and quality of our development process, and has made it easier for us to create and maintain high-quality code for our Flutter projects.