🚀 Mason no Flutter: Escalando seu Boilerplate como um Senior de Verdade
Se você já trabalhou em um projeto Flutter grande, provavelmente já passou por isso:
- Criar pasta de feature manualmente
- Criar cubit, state, page, bindings
- Configurar DI (flutter_getit)
- Criar usecases, repositories…
- Repetir isso dezenas de vezes
Isso não escala.
É aqui que entra o Mason — uma das ferramentas mais subestimadas do ecossistema Flutter.
Segue o repositório no github
https://github.com/brasizza/mason_artigo.git
🧱 O que é o Mason?
O Mason é um gerador de código baseado em templates (bricks).
Você define uma estrutura padrão (boilerplate) e depois simplesmente executa um comando para gerar tudo automaticamente.
Pense nele como:
“um flutter create, só que para qualquer coisa que você quiser”
⚙️ Instalando o Mason
Instalação global via Dart
dart pub global activate mason_cli
mason --version
📦 Inicializando o Mason no projeto
Dentro da raiz do seu projeto Flutter:
mason initIsso vai criar alguns arquivos de inicialização do mason (mason.yaml)
🧱 Criando sua primeira brick
Agora vamos criar uma brick para features:
mason new feature_getitIsso vai gerar:
feature_getit/
├── brick/
└── brick.yamlDentro de __brick__ você define o template:
__brick__/
└── lib/
└── src/
└── features/
└── {{name}}/
├── {{name}}_module.dart
├── domain/
│ ├── entities/
│ │ └── {{name}}.dart
│ └── repositories/
│ └── {{name}}_repository.dart
├── infra/
│ └── {{name}}_repository_impl.dart
└── presentation/
├── {{name}}_page.dart
└── cubit/
├── {{name}}_cubit.dart
└── {{name}}_state.dart🔥 Variáveis e transformação de nomes (ESSENCIAL)
O Mason placehoders ou anotações para os templates.
Você pode transformar variáveis automaticamente:
| Tipo | Exemplo |
|---|---|
| raw | {{name}} → splash |
| PascalCase | {{name.pascalCase()}} → Splash |
| camelCase | {{name.camelCase()}} → splash |
| snake_case | {{name.snakeCase()}} → splash |
💡 Alguns exemplos de arquivos do brick
{{name}}_module.dart
import 'package:flutter_getit/flutter_getit.dart';
import 'domain/repositories/{{name}}_repository.dart';
import 'infra/{{name}}_repository_impl.dart';
import 'presentation/cubit/{{name}}_cubit.dart';
import 'presentation/{{name}}_page.dart';
class {{name.pascalCase()}}Module extends FlutterGetItModule {
@override
String get moduleRouteName => '/{{name}}';
@override
List<Bind<Object>> get bindings => [
Bind.lazySingleton<{{name.pascalCase()}}Repository>(
(i) => {{name.pascalCase()}}RepositoryImpl(dio: i()),
),
Bind.singleton<{{name.pascalCase()}}Cubit>(
(i) => {{name.pascalCase()}}Cubit(repository: i()),
),
];
@override
List<FlutterGetItPageRouter> get pages => [
FlutterGetItPageRouter(
name: '/',
builder: (_) => const {{name.pascalCase()}}Page(),
),
];
}splash_module.dart
import 'package:flutter_getit/flutter_getit.dart';
import 'domain/repositories/splash_repository.dart';
import 'infra/splash_repository_impl.dart';
import 'presentation/cubit/splash_cubit.dart';
import 'presentation/splash_page.dart';
class SplashModule extends FlutterGetItModule {
@override
String get moduleRouteName => '/splash';
@override
List<Bind<Object>> get bindings => [
Bind.lazySingleton<SplashRepository>(
(i) => SplashRepositoryImpl(dio: i()),
),
Bind.singleton<SplashCubit>(
(i) => SplashCubit(repository: i()),
),
];
@override
List<FlutterGetItPageRouter> get pages => [
FlutterGetItPageRouter(
name: '/',
builder: (_) => const SplashPage(),
),
];
}{{name}}_state.dart
part of '{{name}}_cubit.dart';
sealed class {{name.pascalCase()}}State {}
final class {{name.pascalCase()}}Initial extends {{name.pascalCase()}}State {}
final class {{name.pascalCase()}}Loading extends {{name.pascalCase()}}State {}
final class {{name.pascalCase()}}Loaded extends {{name.pascalCase()}}State {}
final class {{name.pascalCase()}}Error extends {{name.pascalCase()}}State {}
splash_state.dart
part of 'splash_cubit.dart';
sealed class SplashState {}
final class SplashInitial extends SplashState {}
final class SplashLoading extends SplashState {}
final class SplashLoaded extends SplashState {}
final class SplashError extends SplashState {}
Esse arquivo define os estados da feature.
Esse é um dos pontos mais acertados da brick
Porque praticamente toda feature assíncrona começa com esse ciclo:
- Initial
- Loading
- Loaded
- Error
Isso cobre boa parte dos fluxos reais.
Por que isso escala bem?
Porque você evita que cada dev invente uma convenção diferente.
Sem brick, um cria:
- Idle
- Fetching
- Success
Outro cria:
- Initial
- Busy
- Done
Outro nem separa estados.
Com a brick, todo mundo começa igual.
{{name}}_repository_impl.dart
import 'package:dio/dio.dart';
import '../domain/repositories/{{name}}_repository.dart';
class {{name.pascalCase()}}RepositoryImpl implements {{name.pascalCase()}}Repository {
final Dio _dio;
{{name.pascalCase()}}RepositoryImpl({required Dio dio}) : _dio = dio;
}splash_repository_impl.dart
import 'package:dio/dio.dart';
import '../domain/repositories/splash_repository.dart';
class SplashRepositoryImpl implements SplashRepository {
final Dio _dio;
SplashRepositoryImpl({required Dio dio}) : _dio = dio;
}Esse arquivo é a implementação concreta do contrato do repository.
Papel desse arquivo
Ele fica na camada de infra porque é aqui que normalmente vivem:
- chamadas HTTP
- acesso a banco local
- adaptação de payload
- mapeamento DTO → entity
- integração com APIs externas
O que sua brick já deixa pronto?
Ela já injeta um Dio, então o repository nasce preparado para acesso HTTP.
Isso é muito bom em projeto real, porque a maioria das features acaba precisando de comunicação externa.
Leitura arquitetural
Esse arquivo representa a parte “que conversa com o mundo”.
Enquanto o domain/repositories define o que precisa ser feito, o infra/…_impl.dart define como será feito.
⚠️ Problema que você resolve com Mason
Sem Mason:
- Código inconsistente
- Devs criando estrutura diferente
- Perda de tempo absurda
- Bugs de DI
Com Mason:
- Padronização total
- Escalabilidade real
- Onboarding rápido
- Código previsível
⚡ Script para criação ultra rápida de features
Agora que você ja tem uma pequena noçao de como o mason pode facilitar sua vida , podemos criar um script simples e ser chamado ./make_feature splash e ele gera a feature completa.
#!/bin/bash
DESTINO_LIB="lib/src/features"
MASON_COMMAND="mason make feature_getit"
if [[ ! -f "pubspec.yaml" || ! -d "lib" ]]; then
echo "❌ Execute na raiz do projeto."
exit 1
fi
if [ -z "$1" ]; then
echo "❌ Informe o nome da feature."
echo "Ex: ./make_feature.sh empresa"
exit 1
fi
FEATURE_NAME=$1
echo "🚀 Criando feature: $FEATURE_NAME"
mkdir -p "$DESTINO_LIB"
$MASON_COMMAND --name "$FEATURE_NAME"
if [ -d "$FEATURE_NAME" ]; then
mv "$FEATURE_NAME" "$DESTINO_LIB/$FEATURE_NAME"
echo "✅ Feature criada em: $DESTINO_LIB/$FEATURE_NAME"
else
echo "⚠️ Verifique se a brick gerou a pasta corretamente."
fiO Mason não é só uma ferramenta.
É um multiplicador de produtividade.
Se você trabalha com Flutter em projetos grandes, não usar Mason é:
aceitar perda de tempo todos os dias


