Flutter — Construindo um Projeto Boilerplate perfeito do zero

Dec 01 2022
Crédito: Nguyễn Thành Minh (Desenvolvedor Android) Todos nós já passamos por isso — o que quero dizer é que todos nós já 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.

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

Arquitetura do projeto

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_envou dart run tools/gen_env.dart. Esta é uma ferramenta que eu mesmo criei, a fim de criar a envpasta para que possamos declarar segredos como Google API_KEY, credenciais básicas de autenticação, etc. Esta ferramenta irá ler valores no .envarquivo e colocá-los no dart-definesinalizador quando o aplicativo Flutter for executado ou construído.

Pasta Env e arquivos .env correspondentes aos seus ambientes

2.3. Gerando arquivos

Uma vez que temos a envpasta, 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 l10najuda a gerar a classe Sa partir dos .arbarquivos nos resourcesmódulos.

Por fim, o comando melos run force_build_allajuda a gerar os arquivos .g.dart, .freezed.dart, etc. em todos os pacotes que possuem a biblioteca build_runner.

Agora, corra make run_devpara 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.

módulo de aplicativo

Ambas as pastas app_icone splashcontêm os arquivos app-icon.yamle 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 apppasta 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 AppBlocpara 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 , basecomo BasePageState, e .BaseBlocBaseEventBaseState

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_imagesã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_scaffoldpodem 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_handlercuidará de todas as exceções no projeto.

A pasta helperconté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

módulo de domínio

Semelhante ao módulo de aplicativo, as pastas confige diservem a propósitos semelhantes no módulo de domínio.

A pasta entitycontém entidades de classe como User e Booking.

A pasta navigationcontém a AppNavigatorclasse, que é responsável por push, replacee poppelas 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

módulo de dados

Da mesma forma, as pastas confige diservem para o mesmo propósito aqui.

A pasta graphqlcontém .graphqle schema.graphqlse o seu projeto usa GraphQL. O arquivo build.yamlé usado para configurar os arquivos gerados relacionados ao GraphQL.

A pasta repositorycontém classes de Implementação de Repositório. Ele também contém várias pastas:

  • converter: para codificar classes do Converter
  • mapper: para codificar classes Mapper que são usadas para mapear modelos de dados em Entidades
  • model: para codificar classes de modelo de dados
  • source: para codificar classes base como RestApiClient, GraphQlApiClient, bem como fornecer fontes de dados no projeto como AppApiService, AppDatabase, AppSharedPreferences, LocationDataSource
Módulo de Recursos

Este é um módulo simples, contém apenas .arbarquivos que fornecem Strings para localização. Este módulo é injetado em outros dois módulos, appe domainassim as classes UI, Bloco, Entidade e Caso de Uso podem utilizar.

3.5. O módulo compartilhado

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

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 .lefthooke o arquivo lefthook.ymlsã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 .envserve para declarar os segredos de cada ambiente.

A pasta toolsconté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 makefileacompanha todos os comandos usados ​​no projeto. Por exemplo, o comando make formatajuda a formatar todo o código com .dartextensão de arquivo dentro do projeto. Existem muitos outros comandos como make testo 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.