Claude Code com Flutter/Dart: mapa do projeto, estado, testes e builds
Fluxo prático para Claude Code em Flutter/Dart: pubspec, widgets, estado, testes, builds e verificação.
Claude Code ajuda mais em Flutter/Dart quando é usado como parceiro que entende o projeto, não como gerador isolado de widget. Uma mudança real pode tocar lib/, pubspec.yaml, assets, testes, configuração de Android e iOS, restrições web, emuladores e comandos de build. Widget é uma peça da interface, state é o dado que altera o que o usuário vê, e pubspec é o arquivo que declara dependências, assets e restrições de SDK.
O exemplo deste guia é uma tela pequena de carrinho. Ela cabe em um projeto criado com flutter create, mas mostra os erros que aparecem em produção: criar estado dentro de build, adicionar pacote sem motivo, esquecer widget tests ou assumir que Android, iOS e Web têm as mesmas regras.
Use a documentação oficial ao adaptar os exemplos: Flutter CLI, Flutter testing, Widget testing, Flutter pubspec options, Dart pubspec, Platform channels e Claude Code common workflows. Leia também React Native, estratégias de teste e boas práticas de CLAUDE.md.
Faça o mapa antes de editar
Não comece com “crie uma tela de carrinho”. Peça que Claude Code leia o repositório sem editar. Esse harness, ou estrutura segura de trabalho, define arquivos permitidos, proibições, comandos de verificação e critérios de revisão.
Leia primeiro este projeto Flutter.
Ainda não edite arquivos.
Informe:
1. Restrições de SDK, dependências e assets em pubspec.yaml
2. Estrutura de UI, estado e camada data em lib/
3. Estilo de testes em test/ e integration_test/
4. Targets ativos em android/, ios/, web/, macos/, windows/ e linux/
5. Se flutter analyze, flutter test e comandos de build estão documentados
Finalize com arquivos seguros para editar e arquivos que não devem ser tocados.
| Área | Claude Code pode fazer | Decisão humana |
|---|---|---|
| Mapa | Convenções, dependências, estilo de teste | Limite de edição |
| Widget | Componente, acessibilidade, responsividade | Texto e design |
| Estado | Seguir setState, ChangeNotifier, Riverpod, Bloc ou padrão existente | Estratégia global |
| pubspec | Propor dependências e assets | Aprovar pacote novo |
| Plataforma | Diferenças Android/iOS/Web/Desktop | Targets suportados |
| Testes/build | Widget test, integration test, comandos | Merge e release |
Trate pubspec como contrato
pubspec.yaml controla resolução de dependências, assets, SDK e pacotes de teste. Antes de editar, Claude Code deve explicar motivo, alternativas, arquivos afetados e comandos de verificação.
name: cart_ai_demo
description: A small Flutter cart screen used to verify Claude Code prompts.
publish_to: "none"
environment:
sdk: ">=3.4.0 <4.0.0"
dependencies:
flutter:
sdk: flutter
dev_dependencies:
flutter_test:
sdk: flutter
integration_test:
sdk: flutter
flutter_lints: ^6.0.0
flutter:
uses-material-design: true
assets:
- assets/images/
Para um sandbox novo, crie a base. Em um projeto existente, peça adaptação aos comandos atuais.
flutter create cart_ai_demo
cd cart_ai_demo
flutter pub get
flutter analyze
flutter test
Os problemas comuns são quebrar a indentação de assets, mudar pubspec.lock sem necessidade e adicionar biblioteca de estado para uma interação simples.
Inspecione pubspec.yaml e decida se a mudança do carrinho precisa de nova dependência.
Antes de editar, mostre motivo, alternativas, arquivos afetados e comandos de verificação.
Não altere pubspec.yaml nem pubspec.lock sem aprovação.
Mude widget e estado em bloco pequeno
O código abaixo pode substituir lib/main.dart em um projeto flutter create cart_ai_demo. Ele usa apenas o SDK Flutter. CartController concentra mudanças de estado; o widget renderiza.
import 'package:flutter/material.dart';
void main() => runApp(const CartDemoApp());
class CartDemoApp extends StatelessWidget {
const CartDemoApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Cart AI Demo',
theme: ThemeData(colorSchemeSeed: Colors.teal, useMaterial3: true),
home: const CartSummaryPage(),
);
}
}
class CartLine {
const CartLine({required this.name, required this.price, required this.quantity});
final String name;
final int price;
final int quantity;
CartLine copyWith({int? quantity}) =>
CartLine(name: name, price: price, quantity: quantity ?? this.quantity);
}
class CartController extends ChangeNotifier {
final List<CartLine> _lines = const [
CartLine(name: 'Dart notebook', price: 1800, quantity: 1),
CartLine(name: 'Flutter sticker', price: 500, quantity: 2),
].toList();
List<CartLine> get lines => List.unmodifiable(_lines);
int get total => _lines.fold(0, (sum, line) => sum + line.price * line.quantity);
void increment(int index) {
final line = _lines[index];
_lines[index] = line.copyWith(quantity: line.quantity + 1);
notifyListeners();
}
void decrement(int index) {
final line = _lines[index];
if (line.quantity == 1) return;
_lines[index] = line.copyWith(quantity: line.quantity - 1);
notifyListeners();
}
}
class CartSummaryPage extends StatefulWidget {
const CartSummaryPage({super.key});
@override
State<CartSummaryPage> createState() => _CartSummaryPageState();
}
class _CartSummaryPageState extends State<CartSummaryPage> {
late final CartController controller;
@override
void initState() {
super.initState();
controller = CartController();
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Cart summary')),
body: AnimatedBuilder(
animation: controller,
builder: (context, _) => ListView(
padding: const EdgeInsets.all(16),
children: [
for (final entry in controller.lines.indexed)
ListTile(
title: Text(entry.$2.name),
subtitle: Text('JPY ${entry.$2.price} x ${entry.$2.quantity}'),
trailing: Wrap(
spacing: 8,
children: [
IconButton(
tooltip: 'Decrease ${entry.$2.name}',
onPressed: () => controller.decrement(entry.$1),
icon: const Icon(Icons.remove_circle_outline),
),
IconButton(
tooltip: 'Increase ${entry.$2.name}',
onPressed: () => controller.increment(entry.$1),
icon: const Icon(Icons.add_circle_outline),
),
],
),
),
const Divider(),
Text('Total: JPY ${controller.total}',
style: Theme.of(context).textTheme.headlineSmall),
],
),
),
);
}
}
Prompt seguro:
Atualize a UI do carrinho, mas mantenha mudanças de estado em CartController.
Não crie CartController dentro de build. Não adicione pacote.
Adicione widget test provando que o total muda ao tocar mais e menos.
Inclua testes e dispositivo
Com o projeto chamado cart_ai_demo, coloque isto em test/cart_summary_test.dart.
import 'package:cart_ai_demo/main.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('updates the cart total when quantity changes', (tester) async {
await tester.pumpWidget(const CartDemoApp());
expect(find.text('Total: JPY 2800'), findsOneWidget);
await tester.tap(find.byIcon(Icons.add_circle_outline).first);
await tester.pump();
expect(find.text('Total: JPY 4600'), findsOneWidget);
await tester.tap(find.byIcon(Icons.remove_circle_outline).first);
await tester.pump();
expect(find.text('Total: JPY 2800'), findsOneWidget);
});
}
flutter analyze
flutter test
Para emulador ou dispositivo real, use integration test.
import 'package:cart_ai_demo/main.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets('cart can be changed on a real device', (tester) async {
await tester.pumpWidget(const CartDemoApp());
await tester.tap(find.byIcon(Icons.add_circle_outline).last);
await tester.pumpAndSettle();
expect(find.text('Total: JPY 3300'), findsOneWidget);
});
}
flutter devices
flutter test integration_test -d <device_id>
Casos de uso e armadilhas de plataforma
Caso 1: adicionar quantidade, filtro ou ordenação em lista existente. Caso 2: limpar pubspec.yaml e assets. Caso 3: investigar problema só em Android, iOS ou Web, como permissões, notificações, localização ou links externos. Caso 4: adicionar testes de regressão a uma tela antiga. Cada pedido deve incluir arquivos-alvo, comando esperado e como reportar falha.
Flutter compartilha Dart, não todas as regras de entrega. Android pode exigir permissões no manifest; iOS envolve Info.plist, CocoaPods e assinatura; Web pode falhar por CORS ou API de navegador; Desktop tem permissões e plugins nativos próprios.
flutter analyze
flutter test
flutter build apk --debug
flutter build web --release
Build iOS release exige macOS e Xcode. Escreva no prompt o que pode ser executado e o que fica pendente.
Prompts seguros e nota prática
Investigação: leia lib/, test/, pubspec.yaml e pastas de plataforma. Não edite. Retorne mapa e riscos.
Implementação: mude só lib/features/cart. Siga o estado existente. Não adicione dependências.
pubspec: se precisar de dependência, pause e mostre motivo, alternativas, impacto e comandos.
Verificação: adicione widget test. Rode flutter analyze e flutter test. Reporte falhas com arquivos.
Revisão: confira dispose, async, efeitos em build, plataforma, acessibilidade, pacotes e testes faltantes.
Nota prática de Masa: os melhores resultados em Flutter vieram de “mapa primeiro, diff pequeno depois, testes e build no fim”. O prompt fraco foi “faça uma tela Flutter bonita”; a UI parecia boa, mas havia estado criado em build, pacote desnecessário e falta de revisão de plataforma. Times podem começar com a cola gratuita e estruturar adoção com treinamento e consultoria Claude Code. Para criar guardrails repetíveis, leia harness engineering.
Os snippets miram flutter create cart_ai_demo. Este ambiente de escrita não tem Flutter SDK instalado, então a verificação final deve acontecer em um ambiente real com flutter analyze, flutter test, build da plataforma alvo e teste em emulador ou dispositivo.
PDF grátis: cheatsheet do Claude Code
Informe seu e-mail e baixe uma página com comandos, hábitos de revisão e workflows seguros.
Cuidamos dos seus dados e não enviamos spam.
Sobre o autor
Masa
Engenheiro focado em workflows práticos com Claude Code.
Artigos relacionados
Workflow Obsidian para CLAUDE.md com Claude Code
Transforme notas de trabalho do Obsidian em notas operacionais CLAUDE.md para preservar contexto.
Claude Code Revenue CTA Routing: artigos para PDF, Gumroad e consultoria
Um fluxo com Claude Code para levar leitores ao PDF grátis, Gumroad ou consultoria conforme intenção.
Regras de handoff para equipes com Claude Code: evidências, permissões, rollback e receita
Formato prático para entregar trabalho do Claude Code com prova, permissões, rollback, PDF grátis, Gumroad e consultoria.