De protótipo a produto: como usar o Flutter 3.22 para validar ideias em 48 horas
Sexta-feira, 16h. A pauta chega no Slack: “precisamos de um MVP para a feira na terça”. O designer já tem o Figma pronto, o PM quer ter algo tocável para validar com usuários e, claro, “é só um fluxo simples, né?”. Se você já passou por isso, sabe que “simples” é o código para “precisamos de milagre”. Há exatamente uma semana, o Google liberou o Flutter 3.22 com melhorias que tornam esse tipo de sprint não apenas possível, mas produtivo. Vou mostrar como transformei esse pedido em um app instalável em menos de dois dias sem cortar cantos que vão me cobrar juros depois.
Por que o Flutter 3.22 importa para MVPs
A versão trouxe duas mudanças discretas que mudam o jogo para quem precisa decolar rápido:
- Tamanho de bundle 15 % menor para Android sem nenhuma ação sua. Menos MB significa download mais rápido na loja interna da empresa e menos decepção quando alguém instala via QR-Code no estande.
- Hot reload 2x mais estável em telas que usam
ListViewaninhado. Se você já perdeu 20 minutos esperando o reload travar em um scroll infinito, sabe o valor disso.
Mas a cereja do bolo mesmo é o Material 3 ganhando padrão. Isso significa que widgets como Switch, Radio e Checkbox já nascem com a aparência do Android 14 e não precisam de customização para parecerem nativos. Economiza horas de CSS-zombie.
O roteiro de 48 horas que usei
Hora 0 - Clonar e colocar para rodar
flutter create feira_mvp --empty
cd feira_mvp
flutter pub add go_router flutter_bloc shared_preferences
Três dependências apenas. go_router para rotas declarativas, flutter_bloc para estado previsível e shared_preferences para mock de “login”. Nada de Firebase ou supabase; vamos tudo em memória.
Hora 1 - Converter o Figma em código
Com o Material 3 ativado, copiei os tokens de cor do Figma para o ColorScheme.light():
final theme = ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.light(
primary: const Color(0xFF005DFF),
onPrimary: Colors.white,
secondary: const Color(0xFFFF5D00),
),
);
Pronto: botões, chips e FAB já nascem com a paleta correta. Sem precisar escrever ElevatedButton.styleFrom em cada canto.
Hora 2 - Gerar o modelo com freezed
Criei o modelo de “Produto” em 30 segundos:
flutter pub add freezed_annotation dev:build_runner dev:freezed
@freezed
class Produto with _$Produto {
factory Produto({
required String id,
required String nome,
required double preco,
@Default(false) bool favorito,
}) = _Produto;
}
Um flutter pub run build_runner build e saíram 200 linhas de boilerplate que eu não precisei digitar.
Hora 4 - Lista fake com mocktail
Para não depender de backend, usei um repositório em memória:
final produtosMock = List.generate(
12,
(i) => Produto(
id: 'p$i',
nome: 'Produto ${i + 1}',
preco: 19.9 * (i % 5 + 1),
),
);
Com mocktail, dá para trocar por API real depois sem tocar nas telas.
Hora 6 - Scroll que não trava
A lista tem imagens grandes; o Flutter 3.22 melhorou o ListView.builder com cacheExtent automático. Bastou envolver as imagens em CachedNetworkImage com placeholder:
CachedNetworkImage(
imageUrl: produto.imagem,
placeholder: (_, __) => const SkeletonAvatar(),
errorWidget: (_, __, ___) => const Icon(Icons.broken_image),
fit: BoxFit.cover,
)
O skeleton veio do pacote shimmer e deixou o loading parecendo Netflix.
Hora 8 - Navegação sem drama
Com go_router, declarei as rotas em 10 linhas:
final router = GoRouter(
routes: [
GoRoute(path: '/', builder: (_, __) => const ListaProdutos()),
GoRoute(path: '/detalhe/:id', builder: (_, state) {
final id = state.pathParameters['id']!;
return DetalheProduto(id: id);
}),
],
);
Deep-link funcionou de primeira. No estande, o PM conseguiu mandar “feira.app/detalhe/p3” por WhatsApp e abriu direto na tela.
Hora 10 - Estado local com Bloc simples
Evitei context.read() espalhado. Criei um ProdutosCubit que expõe apenas:
Future<void> toggleFavorito(String id) async {
final index = state.indexWhere((p) => p.id == id);
if (index >= 0) {
final updated = state[index].copyWith(favorito: !state[index].favorito);
emit([...state]..[index] = updated);
}
}
Com HydratedBloc poderia persistir, mas para MVP ficou em memória. Reiniciar o app limpa tudo, o que é até bom para testes.
Hora 12 - Build release com flavor
Precisava de dois apps: um “demo” com dados engraçados para o estande e um “dev” com logs ligados. Configurei flavors no android/app/build.gradle:
productFlavors {
demo { applicationIdSuffix ".demo" }
dev { applicationIdSuffix ".dev" }
}
Comando para gerar APK do demo:
flutter build apk --flavor demo -t lib/main_demo.dart
APK de 9,8 MB, assinado com debug, mas suficiente para compartilhar via Firebase App Distribution.
Hora 24 - Testes de usabilidade na mesa do café
Instalei o APK em cinco aparelhos diferentes (um Moto G5 e um Galaxy A03 incluídos). O bundle menor fez diferença: 14 segundos de download no 3G do evento. Coletamos 22 gravações de tela com o Lookback e já tínhamos três insights reais para iteração.
Hora 36 - Ajustes rápidos
Com o hot reload estável, alterei o texto de um botão sem perder o scroll position. O designer pediu para trocar o ícone de favorito; foi só alterar Icons.favorite_border para Icons.bookmark_outline e rodar r no terminal. Zero travamento.
Hora 48 - Entrega
Segunda-feira, 17h30. Subi o .aab na Play Console em modo fechado, adicionei 20 e-mails da equipe e enviei o link. Na terça, às 9h, o app estava instalado em todos os tablets do estande. O feedback? “Parece que levou duas semanas”.
Lições que levo para o próximo MVP
- Menos é mais: Três dependências de produção são suficientes para validar.
- Material 3 é seu amigo: Use o que vem de fábrica; customiza depois.
- Flavors salvam vidas: Um comando alterna entre demo, dev e prod.
- Teste no lixo da gaveta: Se rodar num Moto G5, roda em qualquer lugar.
- Hot reload estável vale ouro: Flutter 3.22 entregou isso sem marketing barulhento.
Se a sua próxima sexta-feira parecer impossível, lembre-se: o Flutter 3.22 não faz café, mas transforma um Figma em um app instalável antes do fim de semana. E, quando o produto vingar, você já tem uma base que escala sem reescrever tudo.