Every organization I have worked with was under pressure to release the product sooner, sending faster updates and bug fixes. Although there are many factors that contribute to the development speed, one thing which always slowed me down is writing the boilerplate code. In a usual project, it can consist of up to 30% of the total code. This makes me wonder if there is a way possible to generate these codes. Which brought me to Mason. So what is Mason?

Mason enables developers to create and consume reusable templates called bricks.

Basically, Mason is a template generator where each template is called bricks. Mason provides Mason CLI to create and consume these templates.

Although Mason is written in dart and it’s gaining popularity in the Flutter community but it can be used to generate templates for any language. Let’s see how we can use this in our project to speed up our development.

Installation

Mason can be installed either with the dart pub or brew.

# 🎯 Activate from https://pub.dev
dart pub global activate mason_cli
# 🍺 Or install from https://brew.sh
brew tap felangel/mason
brew install mason

Mason provides quite a few commands to build templates.

Article image

Initialisation

Once you’ve installed mason_cli you can go into the root folder of your project and initialize mason.

mason init

This will generate the mason.yaml file in your project which contains metadata for all your bricks.

# Register bricks that can be consumed via the Mason CLI.
# https://github.com/felangel/mason
bricks:
  # Sample Brick
  # Run `mason make hello` to try it out.
  hello: 0.1.0+1
  # Bricks can also be imported via git url.
  # Uncomment the following lines to import
  # a brick from a remote git url.
  # widget:
  #   git:
  #     url: https://github.com/felangel/mason.git
  #     path: bricks/widget

Bricks 🧱

Bricks simply are customizable reusable templates that can generate code with dynamic variables. These brick templates can be stored locally in the project, remotely in git, or can be published to the brickhub.

Mason uses mustache, a logic less template syntax. To know more about mustache check this documentation.

A new brick template can be created with the mason new command.

mason new -d

mason new cubit -d "A new cubit template"
✓ Created new brick: cubit (0.0s)
✓ Generated 5 file(s):
  /Users/divyanshubhargava/bricks/cubit/brick.yaml (new)
  /Users/divyanshubhargava/bricks/cubit/README.md (new)
  /Users/divyanshubhargava/bricks/cubit/CHANGELOG.md (new)
  /Users/divyanshubhargava/bricks/cubit/LICENSE (new)
  /Users/divyanshubhargava/bricks/cubit/__brick__/HELLO.md (new)

Each brick template consists of the following files and folders.

  1. __brick__ directory: All your template files are added to this Directory.
  2. bricks.yaml: A metadata file for your brick template.
name: cubit
description: A new cubit template
# The following defines the version and build number for your brick.
# A version number is three numbers separated by dots, like 1.2.34
# followed by an optional build number (separated by a +).
version: 0.1.0+1
# The following defines the environment for the current brick.
# It includes the version of mason that the brick requires.
environment:
  mason: ">=0.1.0-dev
 get props => [];
}

{{#snakeCase}}{{name}}_cubit{{/snakeCase}}.dart

import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
part '{{#snakeCase}}{{name}}_state{{/snakeCase}}.dart';
class {{#pascalCase}}{{name}}Cubit{{/pascalCase}} extends Cubit
 {
  {{#pascalCase}}{{name}}Cubit{{/pascalCase}}() : super(const {{#pascalCase}}{{name}}State{{/pascalCase}}());
}

In --test -> cubit -> \{\{#snakeCase\}\}\{\{name\}\}\{\{/snakeCase\}\} we will create a cubit test class.

\{\{#snakeCase\}\}\{\{name\}\}_cubit_test\{\{/snakeCase\}\}.dart

import 'package:bloc_test/bloc_test.dart';
import 'package:mason_demo/cubit/{{#snakeCase}}{{name}}{{/snakeCase}}/{{#snakeCase}}{{name}}_cubit{{/snakeCase}}.dart';
void main() {
  blocTest(
    'emits [] when nothing is added',
    build: () => {{#pascalCase}}{{name}}Cubit{{/pascalCase}}(),
    expect: () => [],
  );
}

Now we are done with our templates let’s add them to mason.yaml.

bricks:
  #Bricks
  cubit:
    path: bricks/cubit

Installing Bricks

To install bricks simply run the mason get command.

Generating code

This is where the magic happens. Every time we need to create a new cubit we can simply run the mason makes cubit command.

mason make cubit
? What's the cubit name? (Dash) Auth
✓ Made brick cubit (0.1s)
✓ Generated 3 file(s):
  /Users/divyanshubhargava/StudioProjects/mason_demo/test/cubit/auth/auth_cubit_test.dart (identical)
  /Users/divyanshubhargava/StudioProjects/mason_demo/lib/cubit/auth/auth_cubit.dart (new)
  /Users/divyanshubhargava/StudioProjects/mason_demo/lib/cubit/auth/auth_state.dart (new)

Voilà, within a fraction of seconds we can generate all our cubit files.

auth_cubit.dart

import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
part 'auth_state.dart';
class AuthCubit extends Cubit
 {
  AuthCubit() : super(const AuthState());
}

auth_state.dart

part of 'auth_cubit.dart';
class AuthState extends Equatable {
  const AuthState();
@override
  List
 get props => [];
}

auth_cubit_test.dart

import 'package:bloc_test/bloc_test.dart';
import 'package:mason_demo/cubit/auth/auth_cubit.dart';
void main() {
  blocTest(
    'emits [] when nothing is added',
    build: () => AuthCubit(),
    expect: () => [],
  );
}

Adding conditions

One thing which I found really helpful while generating code is to able to have conditional code. For example, if you want to generate a new cubit and state class without Equatable. To do so let’s add a new variable calleduseEquatable in our brick.yaml file.

useEquatable:
    type: boolean
    description: Use Equatable
    default: false
    prompt: Do you want to use Equatable?

Now wrap the Equatable code in useEquatable variable.

{{#useEquatable}}
// Your code
{{/useEquatable}}

Finally, this is how our state’s class looks like.

part of '{{#snakeCase}}{{name}}_cubit{{/snakeCase}}.dart';
class {{#pascalCase}}{{name}}State{{/pascalCase}} {{#useEquatable}}extends Equatable{{/useEquatable}} {
  const {{#pascalCase}}{{name}}State{{/pascalCase}}();
{{#useEquatable}}
  @override
  List
 get props => [];
 {{/useEquatable}}
}

Some useful mason commands

List installed Bricks

mason list
//or
mason ls

Add Bricks

mason add

Remove Bricks

mason remove

What’s next?

What we have done here is just the tip of the iceberg. With Mason, you can achieve a lot.

Here is the full working example of this tutorial -> Mason_Demo

I would highly recommend you to check out the mason repo for a more detailed view and the latest changes.

Thank you for reading 👋

I hope you enjoyed this article. If you have any queries or suggestions please let me know in the comments down below.

You can connect with me on Twitter, Github, and LinkedIn. You can subscribe to my newsletter to get an email notification for my latest articles.

Happy Coding… See you next time 👋