Pense Como um Engenheiro Flutter Sênior na Era da IA
Tem um segredo sujo na comunidade Flutter: quando a gente fala em "design de sistemas", a maioria dos engenheiros já começa a desenhar diagramas de balanceadores de carga e filas de mensagens. E olha, eu entendo — foi isso que os livros de preparação para entrevista nos ensinaram. Mas no momento em que você precisa projetar os sistemas do *frontend* — a árvore de widgets, o fluxo de estado, a camada de dados de um app Flutter — a sala fica em silêncio.
O negócio é o seguinte: design de sistemas de frontend é real, é difícil, e é exatamente onde as ferramentas de IA vão fazer você ser dramaticamente mais produtivo ou perigosamente descuidado. Não tem meio termo.
Passei o último ano vendo agentes de codificação com IA gerar apps Flutter em velocidade assustadora. Parte desse código é linda. A maior parte é uma pilha de dívida técnica presa por um `StatefulWidget` que toca três APIs diferentes. Isso não é um problema de ferramenta. É um problema de pensamento.
Vamos resolver isso.
O Que "Design de Sistemas" Realmente Significa Para Flutter
Quando um engenheiro sênior ouve "projete um app de chat", ele não começa com `ListView.builder`. Ele começa com limites.
Engenheiros de backend pensam em serviços, bancos de dados e filas. Engenheiros Flutter precisam pensar em:
- **Decomposição de widgets** — quando um widget de 600 linhas vira uma pasta de widgets focados?
- **Posse de estado** — que estado vive na árvore de widgets, que estado vive em um controller, que estado vive em um repositório?
- **Fluxo de dados** — qual é a direção única dos dados, da rede até os pixels?
- **Tratamento de falhas** — onde ficam as novas tentativas, os fallbacks e os estados de erro?
Um engenheiro Flutter sênior enxerga o app como uma cebola. A camada externa é a árvore de widgets (apresentação). A camada do meio é o gerenciamento de estado (lógica de aplicação). O núcleo é o acesso a dados (repositórios, serviços, armazenamento local). O trabalho do design de sistemas é manter essas camadas sem derreter umas nas outras.
A Regra Que Salva Sua Sanidade: Widgets Não Fazem Negócio
Aqui está uma regra que dou para todo mentorado: **seus widgets não deveriam conhecer sua lógica de negócio, e sua lógica de negócio não deveria conhecer seus widgets.**
Se um `TextFormField` está chamando um repositório diretamente, você quebrou a fronteira. Se seu repositório sabe o que é um `TextEditingController`, você quebrou na direção oposta. Essa separação é o que torna o código testável, substituível e — crucialmente na era da IA — é o que permite que um agente edite código sem explodir seu app inteiro.
Como a IA Muda o Jogo
Ferramentas de IA como GitHub Copilot, Claude Code e Cursor mudaram fundamentalmente como o código é escrito. Eu consigo montar um app CRUD completo em uma tarde. Isso não é exagero; isso é quinta-feira.
Mas aqui está o que aprendi depois de dezenas de projetos Flutter com assistência de IA: **agentes são excelentes para escrever código e péssimos para tomar decisões de arquitetura.**
Dê a um agente uma pasta com um widget incompleto e uma instrução clara, e ele vai acertar. Dê a ele um prompt vago como "deixe esse app melhor", e ele vai silenciosamente tomar quarenta decisões não relacionadas, algumas delas catastrofamente erradas.
É exatamente por isso que a história do agente OpenClaw invadindo o sistema de reservas de uma academia viralizou — não porque o código era inteligente, mas porque o agente recebeu um *objetivo* sem *restrições*. É o mesmo padrão que vejo em codebases Flutter onde agentes foram soltos sem guardrails arquiteturais.
Cenário Um: A Bagunça Gerada por IA
No mês passado, um desenvolvedor que eu mentoro abriu o projeto dele e me mostrou o que um agente tinha produzido. A tarefa era simples: "Adicione suporte offline à tela de pedidos."
O que o agente fez:
- Adicionou `path_provider` e `sqflite` ao `pubspec.yaml`
- Criou um banco de dados SQLite dentro do `initState` de um widget
- Envolveu um cliente HTTP de terceiros em uma classe personalizada sem interface
- Armazenou respostas JSON inteiras em uma variável estática global
Tecnicamente, "funcionou" — no caminho feliz. Mas a conexão com o banco vazava, o cache não tinha estratégia de invalidação, e a UI congelava em dispositivos lentos porque as operações SQLite rodavam na mesma isolate principal.
A correção não eram prompts melhores. A correção era **design de sistemas melhor** — decidir *antes* de o agente escrever código que a persistência viveria atrás de um repositório abstrato, usaria armazenamento seguro em tempo de compilação como o [drift](https://drift.simonbinder.eu), e exporia um stream de estado que a UI apenas observa.
Padrões de Design de Sistemas Que Todo Engenheiro Flutter Deveria Conhecer
Deixa eu te dar o playbook que uso quando sou convidado a projetar um recurso Flutter — seja por um humano, um agente, ou uma combinação dos dois.
1. O Bolo de Três Camadas (Apresentação / Aplicação / Dados)
- **Camada de apresentação**: widgets, animações, roteamento. Ela só consome estado e emite intenções (intents).
- **Camada de aplicação**: gerenciamento de estado (providers do Riverpod, cubits do Bloc, ChangeNotifiers). Ela transforma intenções em mudanças de estado.
- **Camada de dados**: repositórios, serviços, bancos de dados locais, clientes de rede. Ela cuida do trabalho sujo de falar com o mundo externo.
Cada camada só fala com a camada diretamente abaixo. Widgets nunca tocam no `Dio`. Repositórios nunca retornam `BuildContext`.
Se você está se perguntando "onde fica a validação de formulário?" ou "devo colocar a chave de API no Provider?" — a resposta é sempre "na camada que é dona dessa preocupação."
2. Gerenciamento de Estado como um Fluxo Unidirecional
Não importa qual biblioteca de gerenciamento de estado você escolha — [Riverpod](https://riverpod.dev), [Bloc](https://bloclibrary.dev), ou até mesmo `InheritedWidget` puro — desde que o fluxo seja de uma direção só:
```
Stream de Estado -> Widgets Renderizam -> Intenções do Usuário -> Mutações de Estado -> Repetir
```
No momento em que você tem bindings de duas vias, você tem bugs. No momento em que widgets mutam estado diretamente, você tem bugs que não consegue reproduzir. No momento em que você tem um `static var` global segurando dados de usuário, você tem uma revisão de segurança no seu futuro.
3. Limites de Erro e Degradação Graciosa
Um engenheiro sênior projeta para a falha primeiro. Cada tela responde a estas perguntas:
- O que acontece se a API cair?
- O que acontece se o usuário não tiver internet?
- O que acontece se a estrutura do JSON mudar?
- O que acontece se o dado for nulo?
Isso não é sobre envolver tudo em try-catch. É sobre projetar *tipos de estado* que incluam estados de `loading`, `loaded`, `error` e `empty` — e então garantir que todo widget saiba renderizar os quatro. Eu prometo que isso vale mais do que qualquer pacote obscuro.
A Abordagem "Spec First" para Flutter com Assistência de IA
Eu mencionei que agentes de IA se perdem sem restrições. A correção que descobri funcionar de forma consistente é o **desenvolvimento orientado a especificação**. Escreva a especificação *antes* de o agente começar a codar.
Aqui está o fluxo de trabalho:
1. **Escreva um Registro de Decisão de Arquitetura (ADR) de uma página** para o recurso. Mantenha abaixo de 500 palavras. Inclua a estrutura da árvore de widgets, os hooks de gerenciamento de estado e os contratos de dados.
2. **Defina as fronteiras de arquivos**. Nomeie as pastas: `features/checkout/widgets/`, `features/checkout/logic/`, `features/checkout/data/`. Dê ao agente os caminhos de arquivo explícitos.
3. **Instrua o agente a pedir decisões de arquitetura para você, não tomá-las**. Essa frase evita 90% das histórias de terror de arquitetura dirigida por IA.
4. **Revise o código do agente contra o ADR antes de executá-lo**. Revisão de código continua sendo seu melhor teste.
Isso não é burocracia. É garantir que as 500 linhas de código do agente caiam no lugar certo. Porque a alternativa é refatoração — e refatorar código gerado por IA é pior do que refatorar código humano, porque *ninguém* o escreveu com intenção.
Cenário Dois: Respostas de IA em Streaming (Sim, Você Está Construindo um Recurso de IA)
Você está construindo um recurso de chat com IA. O agente consegue gerar uma tela de respostas em streaming lindamente animada em minutos. Mas dê um passo atrás e pense em nível de sistemas:
- Onde vive o histórico da conversa? Memória, armazenamento local, servidor?
- O que acontece quando o stream HTTP é interrompido no meio da resposta?
- Como você limita a taxa de requisições e lida com erros 429?
- Como você lida com conteúdo inseguro ou alucinado na UI?
Um engenheiro sênior projeta o estado de streaming (`StreamBuilder`, estado com buffer, lógica de retry) antes de escrever um único widget. O agente preenche o resto.
Cenário Três: Um App de Tarefas Offline-First
Crie um app onde usuários gerenciam tarefas em um avião. A pergunta de design de sistemas é: você otimiza para nunca perder dados, ou para velocidade?
Com um banco de dados local baseado em `drift`, um repositório que sincroniza com uma API REST, e um provedor de conectividade que monitora o estado da rede, o design continua limpo. O agente escreve o encanamento; o engenheiro sênior projeta a *forma* desse encanamento.
O Que a IA Torna "Desnecessário" (E O Que Ela Não Torna)
Houve uma onda de artigos de opinião afirmando que a IA vai eliminar a necessidade de desenvolvedores humanos. O argumento a favor é que ela dá mais autonomia aos criadores; o argumento contra é que ela expõe as lacunas que a autonomia torna mais difíceis de esconder — Andy Budd escreveu sobre isso para design digital, e é exatamente verdade para Flutter: a IA remove o *atrito* de escrever código, mas não a *responsabilidade* de saber o que escrever.
A IA pode:
- Gerar código boilerplate de widgets mais rápido do que você digita
- Traduzir um mockup de design em uma árvore de widgets em segundos
- Escrever testes unitários para seus repositórios
A IA não pode:
- Decidir onde o estado vive
- Decidir o trade-off entre frescor do cache e custo de API
- Decidir como deve ser o skeleton de carregamento quando a API está lenta
Essas são decisões de design. Elas são *seu* trabalho, e vão continuar sendo seu trabalho daqui a dez anos.
Uma Checklist Rápida Para Seu Próximo Design de Sistemas Flutter
Quando alguém pedir para você projetar um app Flutter — em uma entrevista, em um documento de design, ou apenas na sua cabeça — rode esta checklist:
1. **Quais são os átomos funcionais?** Divida o app em recursos. Cada recurso deve ser construível, testável e removível de forma independente.
2. **Quais são os átomos de estado?** Para cada recurso, identifique: que estado é local, que estado é compartilhado, e que estado é persistido?
3. **Como os dados entram e saem?** Toda fonte de dados ganha um repositório. Todo repositório retorna objetos tipados, não JSON cru.
4. **O que acontece quando quebra?** Estados de falha para toda chamada assíncrona. Estados de carregamento para todo stream.
5. **Como um novo desenvolvedor (ou um agente de IA) encontraria o código?** A estrutura de pastas deveria ser autoexplicativa.
Mantendo os Pés no Chão: O Princípio do "Bom o Suficiente"
Não quero que você termine este post achando que seu app precisa de seis camadas de abstração antes de poder adicionar um botão de login. Design de sistemas é sobre *proporção*.
Um app utilitário de duas telas não precisa de Riverpod com geração de código. Ele precisa de um `StatefulWidget` e um repositório. Um app de e-commerce em produção não precisa de trinta providers para renderizar uma lista de produtos. Ele precisa de fronteiras claras, posse de estado deliberada, e uma camada de dados que não vaze para a UI.
Bons engenheiros seniores sabem quando *não* aplicar complexidade. Agentes de IA não sabem — eles vão felizes gerar um padrão de factory para um recurso que poderia ter quarenta linhas. Seu trabalho é ser a salvaguarda.
Considerações Finais
Aqui está o que separa engenheiros Flutter juniores de seniores na era da IA: **juniores pedem para a IA construir o app, e depois tentam consertar o que quebra. Seniores projetam as fronteiras do app, e depois deixam a IA construir dentro delas.**
Adote a mentalidade de três camadas. Escreva especificações curtas antes de soltar os agentes. Revise com intenção. Projete para a falha primeiro. E lembre-se: o sistema é sobre *o que você está construindo*, não sobre a IA que o escreve.
Porque seja um humano ou um modelo de linguagem digitando o código, alguém ainda precisa decidir onde o estado vive. Esse alguém deveria ser você.
FAQ
1. Eu realmente preciso de "design de sistemas" para Flutter? Isso não é coisa de backend?
Design de sistemas de frontend é absolutamente real. É sobre decidir onde o estado vive, como os dados fluem, e onde colocar as fronteiras entre widgets, lógica e camadas de dados. Design de backend lida com escala; design de frontend lida com *complexidade*. Um app Flutter com cinquenta widgets interdependentes e sem fronteiras é tão difícil de manter quanto um backend com serviços espaguete.
2. Como devo abordar design de sistemas em uma entrevista de Flutter?
Foque em separação de preocupações. Entrevistadores querem ouvir você falar sobre decomposição da árvore de widgets, posse de estado (o que é local vs. global), contratos de dados (o que o repositório retorna?) e tratamento de falhas. Mencionar ferramentas como Riverpod ou Bloc é bom, mas dizer *quando você usaria cada uma* é melhor. E não se apresse para codar — fale sobre os trade-offs primeiro.
3. Devo deixar agentes de IA projetarem minha arquitetura Flutter do zero?
Não. Agentes são ótimos para implementar dentro de fronteiras, mas vão felizes colocar uma chamada de banco de dados em um widget se o prompt for vago. Escreva uma especificação curta — até mesmo tópicos — definindo pastas, hooks de gerenciamento de estado e contratos de dados *antes* de o agente começar. Depois revise a saída contra ela. Sua especificação é a alavanca de controle mais importante.
4. Riverpod ou Bloc é melhor para apps Flutter grandes?
Ambos estão prontos para produção; isso é mais uma questão de filosofia do que de tecnologia. Riverpod oferece segurança em tempo de compilação, providers com escopo e um estilo mais funcional. Bloc impõe um padrão estrito de evento/estado que é mais fácil de documentar ao custo de verbosidade. Para apps grandes, escolha com base no conforto do seu time e depois seja consistente. Aplicar qualquer um de forma consistente é melhor do que trocar no meio do projeto. Leia as duas documentações — [riverpod.dev](https://riverpod.dev) e [bloclibrary.dev](https://bloclibrary.dev) — e tomem a decisão com seu time.
Tecnologia
Comments (0)
No comments yet. Be the first to comment!
Leave a Comment