Flutter — Construindo um Projeto Boilerplate perfeito do zero

Crédito: Nguyễn Thành Minh (Desenvolvedor Android)
Todos nós já passamos por isso - o que quero dizer é que todos nós tentamos construir um projeto clichê para o trabalho ou como um projeto pessoal. Um bom projeto clichê nos poupará muito tempo resolvendo problemas comuns como chamar APIs, processar dados de bancos de dados e talvez o mais importante — tornar nosso código organizado e consistente para que as pessoas possam ter mais facilidade em entender nosso código. Esta série destina-se a compartilhar um projeto padrão que construí, usei e mantive nos últimos dois anos. Além disso, compartilharei minha abordagem para codificá-lo, bem como soluções para problemas comuns que possam surgir. Dito isso, esta é a parte 1: Explorando o Projeto.
Veja no Github

1. Características
Funcionalidades construídas neste projeto:
- Arquitetura: Arquitetura Limpa
- Gerenciamento de estado: flutter_bloc
- Navegação: auto_route
- DI: get_it , injetável
- API REST: di
- GraphQL: artemis , graphql_flutter
- Banco de dados: objectbox
- Preferências compartilhadas: crypto_shared_preferences
- Classe de dados: congelado
- Lint: dart_code_metrics , flutter_lints
- CI/CD: Github Actions, Bitbucket Pipelines
- Teste de Unidade: mocktail , bloc_test
- Paginação: infinite_scroll_pagination
- Utilitários : rxdart , dartx , assíncrono
- Gerador de ativos: flutter_gen_runner , flutter_launcher_icons , flutter_native_splash
- Outros recursos: solicitações de API com falha na repetição automática, token de atualização,…
Para executar este projeto como pretendido, sua versão do Flutter deve ser (exatamente) 3.3.9 e a versão Melos 2.8.0 deve estar instalada. Se você gostaria de ter uma Convenção Git para sua equipe, você também pode instalar o lefthook . Falarei sobre Melos e Lefthook nas últimas partes da série. Por enquanto, veja como executar o projeto.
Nota: usei VSCode e macOS, então não posso garantir que também funcione no Android Studio ou Windows.
2.1. Instalando Melos
A Arquitetura Limpa exige que dividamos o projeto em vários módulos (pacotes), por isso precisamos de uma ferramenta para gerenciar pacotes — o Melos. Para instalar Melos:
dart pub global activate melos 2.8.0
export PATH="$PATH:<path to flutter>/flutter/bin"
export PATH="$PATH:<path to flutter>/flutter/bin/cache/dart-sdk/bin"
export PATH="$PATH:~/.pub-cache/bin"
Depois de instalar o Melos, você precisará executar o comando make gen_env
ou dart run tools/gen_env.dart
. Esta é uma ferramenta que eu mesmo criei, a fim de criar a env
pasta para que possamos declarar segredos como Google API_KEY, credenciais básicas de autenticação, etc. Esta ferramenta irá ler valores no .env
arquivo e colocá-los no dart-define
sinalizador quando o aplicativo Flutter for executado ou construído.

2.3. Gerando arquivos
Uma vez que temos a env
pasta, agora só precisamos executar o comando make sync
. Esta é uma abreviação para três comandos:
melos bootstrap
melos run l10n
melos run force_build_all
Em seguida, o comando melos run l10n
ajuda a gerar a classe S
a partir dos .arb
arquivos nos resources
módulos.

Por fim, o comando melos run force_build_all
ajuda a gerar os arquivos .g.dart
, .freezed.dart
, etc. em todos os pacotes que possuem a biblioteca build_runner
.
Agora, corra make run_dev
para rodar o app e aproveite!
3. Arquitetura do Projeto
Ao todo, temos 6 módulos (pacotes)
- Aplicativo
- Domínio
- Dados
- Recursos
- Compartilhado
- Inicializador
3.1. O módulo de aplicativo
É aqui que codificaremos nossa interface do usuário e o Bloco. Além disso, é onde armazenaremos nossos recursos como cores, dimensões e estilos de texto. Também é aqui que declararemos nossa função main() como um ponto de entrada para a execução de nosso aplicativo.

Ambas as pastas app_icon
e splash
contêm os arquivos app-icon.yaml
e splash.yaml
, portanto, podemos configurar o ícone do aplicativo e a tela inicial conforme as instruções das bibliotecas flutter_launcher_icons e flutter_native_splash .
A app
pasta contém AppBloc
, que é um Bloco fornecido em MaterialApp
, um local de onde todas as telas do app podem recuperar dados, ou seja; é como um bloco compartilhado para todas as telas. Você pode usar AppBloc
para tarefas que precisam atualizar a interface do usuário de todas as telas, como configuração de idioma, alternância de modo escuro/modo claro.
A pasta é onde codificaremos as classes base , base
como BasePageState
, e .BaseBloc
BaseEvent
BaseState
A pasta shared_view
é onde codificarei a interface do usuário compartilhada entre várias telas e poderá ser usada para outros projetos. Por exemplo: app_text_field
, circle_image
são visualizações de grupo comuns vistas nas telas de Login e Cadastro.
A pasta common_view
é onde codificarei a interface do usuário compartilhada entre várias telas e poderá ser usada para outros projetos. Por exemplo, common_dialog
, common_app_bar
, common_scaffold
podem ser reutilizados em muitos projetos, embora possamos ajustá-los um pouco para corresponder ao estilo.
A pasta config
é onde chamarei funções de configuração para o módulo de aplicativo, comoFirebase.initializeApp()
A pasta di
é para configuração de DI para o módulo app.
A pasta exception_handler
cuidará de todas as exceções no projeto.
A pasta helper
contém classes Helper usadas exclusivamente para o módulo app.
A pasta navigation
é onde declararemos as telas, bem como as caixas de diálogo e as planilhas inferiores usadas no aplicativo.
A pasta resource
é onde declararemos recursos para a interface do usuário, como cores, dimensões e estilos de texto.
A pasta ui
é onde codificarei a interface do usuário e o bloco para cada tela.
A pasta utils
é onde codificarei as funções utils usadas exclusivamente para o módulo app.
3.2. O módulo de domínio

Semelhante ao módulo de aplicativo, as pastas config
e di
servem a propósitos semelhantes no módulo de domínio.
A pasta entity
contém entidades de classe como User e Booking.
A pasta navigation
contém a AppNavigator
classe, que é responsável por push
, replace
e pop
pelas telas.
A pasta repository
é onde declararemos as classes do Abstract Repository no projeto.
A pasta usecase
é onde declararemos os Casos de Uso no projeto.
3.3. O módulo de dados

Da mesma forma, as pastas config
e di
servem para o mesmo propósito aqui.
A pasta graphql
contém .graphql
e schema.graphql
se o seu projeto usa GraphQL. O arquivo build.yaml
é usado para configurar os arquivos gerados relacionados ao GraphQL.
A pasta repository
contém classes de Implementação de Repositório. Ele também contém várias pastas:
converter
: para codificar classes do Convertermapper
: para codificar classes Mapper que são usadas para mapear modelos de dados em Entidadesmodel
: para codificar classes de modelo de dadossource
: para codificar classes base como RestApiClient, GraphQlApiClient, bem como fornecer fontes de dados no projeto como AppApiService, AppDatabase, AppSharedPreferences, LocationDataSource

Este é um módulo simples, contém apenas .arb
arquivos que fornecem Strings para localização. Este módulo é injetado em outros dois módulos, app
e domain
assim as classes UI, Bloco, Entidade e Caso de Uso podem utilizar.
3.5. O módulo compartilhado

Como o nome sugere, este módulo fornece constantes, classes de exceção personalizadas, classes auxiliares, mixins e funções utilitárias para outros módulos usarem.
3.6. O módulo inicializador

Este módulo contém apenas a classe AppInitializer. Esta classe é responsável por reunir todas as configurações de outros módulos em uma init()
função; A main()
função só precisa chamar esta init()
função para recuperar todas as configurações de todos os módulos.
3.7. Outras pastas e arquivos
A pasta .github
é para configurar CI/CD se o seu projeto usar o Github Actions.
A pasta .lefthook
e o arquivo lefthook.yml
são para configurar a convenção Git usando a biblioteca lefthook.
A pasta .vscode
é para declarar as configurações do VSCode usadas para este projeto.
A pasta .env
serve para declarar os segredos de cada ambiente.
A pasta tools
contém as ferramentas que criei.
O arquivo analysis_options.yaml
é para declarar regras linter de duas bibliotecas flutter_lints e dart_code_metrics .
O arquivo bitbucket-pipelines.yml
é para configurar o CI se o seu projeto usar o Bitbucket Pipelines.
O arquivo makefile
acompanha todos os comandos usados no projeto. Por exemplo, o comando make format
ajuda a formatar todo o código com .dart
extensão de arquivo dentro do projeto. Existem muitos outros comandos como make test
o que executa o Unit Test em todos os módulos.
O arquivo melos.yaml
é para configuração do Melos.
Conclusão
Por que precisamos criar outro módulo apenas para init config? O que são mapeadores, entidades e classes de caso de uso? Irei abordá-los na segunda parte desta série — a Arquitetura Limpa.