Um pouco sobre o GetX Pattern ! 🚀

Kaue Murakami
8 min readJul 5, 2021

Falando um pouco mais sobre o desenvolvimento e uso do queridinho getx_pattern 🚀 !

Há algum tempo me vi perdido ao estudar Flutter, mais especificamente, ao me deparar com seus diversos modos de gerenciar estados e seus diversos packages disponíveis para isso;

Então descobri o GetX, com o qual eu, mais me identifiquei, mas mesmo com sua escrita fácil e intuitiva, ainda faltava algo … sabe, aquela coisinha.

Como organizar meu código, de maneira que seja equivalente ao uso do mesmo, de forma modular, escalável e antes de mais nada, simples e clean !

Trazendo conceitos da arquitetura mvc, mas com um paradigma reativo, resolvi, o que viria a ser meu problema e de muitas outras pessoas. Aproveitando pra dizer que já fiz o uso do mesmo com outros gerentes de estados também e atualmente testando no back-end com NodeJS !

A ideia era que pudéssemos manter e organizar nosso projeto, pensando no mínimo que uma aplicação precisa para funcionar, de forma que qualquer alteração ou “adição” (new feature) que ocorra, venha a ser toda controlada por um único módulo, ou compartilhada para ser controlada por mais de um, mas partindo de apenas um lugar.

Aqui vou utilizar um exemplo de um projeto de estudos pessoal, algo relacionado a delivery 😳, mas não vem ao caso, vamos nos atentar a organização apenas.

Começando pela estrutura geral, levando em consideração que você já entenda a estrutura inicial do Flutter, a primeira diferença encontrada é a pasta assets, ela é responsável por arquivos como fontes, imagens, ícones ou ate mesmo um arquivo json estático que seja utilizado no aplicativo, este diretório nunca conterá nenhum código, mesmo estáticos como classes abstratas, isso fica em outro local, logo chegaremos lá 🙃.

Além disso, em lib, pasta onde escrevemos a maioria dos nossos códigos, temos 3 diretórios e 1 arquivo, os quais vamos nos atentar a partir de agora. Sendo eles:

📲 app : Onde contém todos os nosso módulos e dados, que nossa aplicação precisa para funcionar, vamos falar detalhadamente deles na próxima seção.

❤️ core : Aqui vão ficar nossos arquivos de código estáticos da aplicação, como classes abstratas que são utilizadas em algum momento por nossos módulos, arquivos vitais como tema, strings, estilos de texto,esquema de cores e internacionalização.

✈️ routes : Aqui é mais simples, possuímos nossa classe de rotas, veremos mais detalhes quando chegarmos nos exemplos, onde guardamos nossas rotas como constantes, dessa forma, qualquer alteração da rota na classe, é refletida em toda aplicação. Juntamente com nosso arquivo de pages e/ou views, ou o sistema de roteamento que você utilizar, aqui vamos utilizar o do próprio GetX para o exemplo, você também pode usar o do próprio Flutter.

🎩 main : Simplesmente nosso arquivo principal, esses todos conhecemos bem 🤓.

Explorando essas três pastinhas 👀

app
core
routes

Lembrando que você nem sempre vai precisar de todas essas pastas, mesmo sendo poucas, em relação aos demais design patterns/architectural patterns, é sempre bom lembrar que nem todas as aplicações são iguais, mesmo utilizando o mesmo padrão, umas são mais simples do que esperamos.

Explorando o conteúdo do nosso diretório app

Como dito rapidamente anteriormente, aqui contém todos os módulos e dados necessários para nossa aplicação funcionar.

data

Representando os dados da nossa aplicação, o diretório data contém todos os nossos models, enums, provedores de dados e services.

Models: Nossos bons e velhos modelos/classes, abstrações dos nossos objetos do mundo real.

enums: Nossos enums, geralemente utilizados para variáveis categóricas, ex: Payment.pix, Payment.creditCard.

providers: Provedores de dados é tudo aquilo que é responsável por nos fornecer os dados necessários para aplicação, caso possua, pode vir de uma api, um socket, um banco de dados, ou de mais de um!

services: Um service é responsável por conter dados e/ou funcionalidades partilhadas em mais de um módulo, lembra que falamos disso no começo ? Pois é, o Service é o cara !

Exemplo de uso da pasta data

No nossa exemplo, possuímos algumas classes, e um enum, além de alguns providers e services, dos quais vou falar um pouco mais.
Repare que neste exemplo possuímos mais de um provedor de dados, pois além da api, também salvo dados offline e tenho um socket para uso de um chat. Você poderia apenas ter um deles, ou nenhum se sua aplicação não depender de nada disso.
Como dito sobre os services, eles possuem algumas responsabilidades que podem ser compartilhadas, vou falar bem rápido sobre elas, apenas para fixar o exemplo.
Meu AuthService é responsável por controlar tudo relacionado ao meu usuário, como o próprio objeto, login, logout, cadastro, dessa forma posso recuperar meu usuário de vários módulos apenas recuperando esse serviço, no caso do meu CartService é um carrinho de compra que quero manter do mesmo jeito até determinado ponto da minha aplicação, você não quer dar um back e perder tudo que está lá né rsrs, e também posso acessar os dados armazenados nele (meu carrinho de compras) em mais de um módulo.
Meu ConfigService é responsável por alguma configurações simples do meu app, como verificar se é o primeiro acesso daquele dispositivo, alterar o tema, e posso fazer isso, novamente, em mais de um módulo .

Módulos

Aqui é tudo mais simples e intuitivo, cada módulo é uma tela em questão, atribuímos a responsabilidade de cada módulo à um controller, em casos especiais, caso haja necessidade você pode usar mais de um controller, mas normalmente apenas um é suficiente.
Além da nossa page (tela) e controller (controlador), temos dependências que precisam estar presentes na inicialização dos mesmos, para isso temos nosso binding, um injetor de dependências do GetX, que usa lazy initialization para injetar nossas dependências assim que nossa page for chamada, com ele podemos injetar nosso controller, provider e repository, do qual vamos falar agora.
O conceito de repository, aqui foi levado ao pé da letra, ele estará presente em nosso módulo apenas se o mesmo tem alguma dependência/funcionalidade que necessite de um provider, caso contrário, não há necessidade de um repository, como por exemplo, uma splash screen simples.
Além disso, você deve ter reparado na nossa pasta widgets, no mesmo nível dos módulos, isso porquê alguns widgets, também podem ser utilizados em vários módulos de maneira independente, como por exemplo, loadings, um alert padrão, botões e etc… O mesmo vale para cada módulo internamente, para que aquela nossa page complexa não possua 400 linhas ( ou + ), e prejudique tanto a leitura do código quanto a manutenção do mesmo, podemos/DEVEMOS dividir essa página em vários widgets, se você não está habituado a isso, comece dividindo por seções, imagine uma top_section.dart, middle_section, bottom_section, comece reutilizando um mesmo botão por toda a aplicação com uma callback genérica, caso precise de dados do controllador um final controller = Get.find<Controller>(), irá te ajudar.
No exemplo abaixo mostro algumas abordagens que discuti acima, como componentização de uma page em vários widgets internos e externos ao módulo, repare também que meu módulo cart não possui um repository, isso pois como dito sobre os services, quem vai interagir mesmo com nosso provedor é o CartService, que é recuperado com um find, já mencionado antes, no controller do nosso módulo cart.

Repare também na nomenclatura dos arquivos e suas pastas, todos seguem o mesmo padrão, para manter a visualização mais clara e menos poluída, com um módulo nomeado não há necessidade de nomear os arquivos com estabelecimento_controller, estabelecimento_page, você consegue destingui-los pelo nome do módulo, simples assim.

exemplo de um conjunto de módulos diferentes
Widgets reutilizados por mais de um módulo

Core

Nosso diretório core é simples de entender, ele contém a seguinte estrutura básica.

core

Nele podemos ter configurações (configs), como alguma configuração de banco de dados, temas (theme), podendo ser um app_theme, tema para toda a aplicação, ou temas para textos, um arquivo com constantes TextStyle, é bem útil quando utilizamos o mesmo tema diversas vezes, isso facilita a manutenção, juntamente com os nosso values, diretório herdado do Android nativo, contém arquivos como colors.dart, strings.dart, que nada mais são do que nossas strings státicas no app, o mesmo vale para as cores, imagine mudar seu esquema de cores, estilo de todos os titulos e até mesmo o título sem essa centralização na sua aplicação, seria página por página, em values também podemos manter nosso diretório de languages, para internacionalização, por estar ao lado das nossas strings, apontando nossas constantes para que nossa internacionalização também receba essas alterações, também keys.dart, onde armazenamos nossas keys, utilizo bastante para keys de storage e chaves constantes que precisamos na aplicação.
Por último, mas não menos importante, nosso diretório de utils (utilitários), basicamente são helpers que possuem exclusivamente uma responsabilidade, por exemplo, uma extension para captalize (deixar a primeira letra maiúscula), ou uma classe que retorna seus headers para utilizar nas chamadas dos seus provider com um token opcional.

class HeadersAPI { final token; HeadersAPI({this.token}); Map<String, String> getHeaders() {  return {   "Content-Type": "application/json-patch+json",   "Authorization": "Bearer ${this.token}"  }; }}
exemplo de core

Routes

E finalmente nosso diretório routes, o mesmo é composto apenas por dois arquivos que fazendo parte um do outro com o uso do part of do Flutter.

routes

Como já fora explicado anteriormente, nosso arquivo routes.dart é responsável por armazenar nossas “urls” de navegação.

exemplo de routes

E nosso arquivo pages.dart é nossa lista de páginas com sua respectiva rota, page e binding, os componentes básicos de um módulo 😁.

exemplo da um arquivo pages

Apenas para não faltar nada, vou deixar um print da main.dart, pros que não estão habituados com a configuração inicial.

Bem, por hoje é só 🏆.

Nunca dei as caras para falar um pouco sobre esse meu projeto, que é meu preferido, pelo fato de poder manter uma linguagem universal de estruturação de código no momento das pessoas conversarem sobre isso, além de ser um norte pra quem começa no getx, está satisfazendo as necessidades de muitas pessoas e empresas, sendo muito bem adotado tanto dentro quanto fora do nosso Brasil, prometo que logo trarei alguns vídeos tutoriais dependendo, do feedback desta publicação.
Também espero ter esclarecido algumas dúvidas, caso tenha ficado alguma meu contato está disponível no github, linkado no inicio deste artigo.

Até mais 🌝

--

--