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.
Artigos relacionados
Claude Code e React Native: guia prático de Expo, nativo e release
Use Claude Code com React Native: Expo vs bare, permissões, Metro, emuladores, acessibilidade e checks de release.
Onboarding de desenvolvedores com Claude Code: de meses para 2 semanas
Fluxo prático com CLAUDE.md, permissões, CI, checklist do primeiro PR e template de review.
Desenvolvimento com Storybook e Claude Code: UI, testes e CI
Configure Storybook com Claude Code: stories tipadas, interaction tests, CI e revisão visual com Chromatic.
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.