Use AI for a Flutter app without spending longer debugging the generated code

Noor leans across a wooden desk and holds a small phone toward Ivo, who hugs a large tablet to his chest beside a stand holding three larger phones.

TL;DR

Generated Flutter code can pass the analyzer and still cost time where nothing on screen shows it: state that outlives its widget, a layout too wide for a small phone, a state management style nobody chose. Hand over one feature slice at a time, name the state approach and the files it may touch, and require widget tests for an early exit and a narrow screen to pass before it reports done. Then check on a device and time the whole task, not the first build.

Where the saved time goes back out

The most useful answer in a September 2025 Flutter community forum thread, asking for the best Claude Code setup for Flutter, was not about the tool. While other replies argued over which agent was better, one described a routine: keep each task small rather than one-shotting large features, give the agent samples of the structures you like, ask it to plan first and correct the plan, and have it generate tests. They used it for "banging out pages and widgets" where some sloppiness was tolerable, and not for lower-level or tricky work.

In April 2026 a developer on the Cursor forum asked which model makes fewer mistakes in Flutter, and whether to build in one long conversation or split the project into smaller modules. The replies disagreed about models, and those that addressed workflow pointed the same way: smaller modules, one task per chat, tests first.

Neither thread says which model wins, and this article does not either. Both point at the same levers: how much the agent is given at once, how much of the design it is left to guess, and which checks it has to pass before you look. Flutter makes the last one matter, because some of its faults stay silent until a user does something slightly awkward.

Decide what the agent may touch before it starts

Flutter's own architecture guide strongly recommends keeping logic out of widgets, in view models, and calls the choice of state management library a matter of preference. If the brief does not make that choice, the agent makes it each time: one feature can arrive with setState and the next with a new provider, and the seams between them become places for bugs to hide.

A useful split is between work you can describe completely and decisions that shape the rest of the app:

Comparison

Hand over, with an existing example to copyDecide yourself first, then let the agent type it
Layout and stateless widgets inside one screenWhich object owns the state, and how widgets listen to it
Model classes and JSON mappingWhat happens to in-flight work when the user leaves
Widget and unit tests for behaviour you wrote downNavigation, deep links and back behaviour
Repository code against a documented APIPlugins, platform channels and permissions

Then write the brief around that boundary:

  • The state approach the app already uses, and one finished feature to copy.
  • The files it may create or change, and the ones it must leave alone: router, dependency setup, theme, shared widgets.
  • The behaviour, including the awkward cases: slow network, the user leaving mid-request, a small phone, large text.
  • The commands that must pass before it reports done.

A change that cannot be briefed this way is usually too big for one run. The article on splitting a large feature into tasks an AI coding agent can finish reliably covers how to cut it.

One small feature, four versions, three checks

To see which checks catch which faults, a small stateful feature was written for this article: a product row with a "Save for later" button that calls an asynchronous repository, shows progress and then "Saved". It was run with Flutter 3.35.3, Dart 3.9.2 and the default flutter_lints 5.0.0 rules (the documentation is now at Flutter 3.47). Two familiar Flutter faults were seeded on purpose: no mounted check after the await, and a title Text in a Row without Expanded. Three widget tests covered the normal tap, a user who leaves the screen before the save finishes, and a 320-point-wide phone with text at 130%:

tester.view.physicalSize = const Size(960, 1920);
tester.view.devicePixelRatio = 3.0;
tester.platformDispatcher.textScaleFactorTestValue = 1.3;

The analyzer reported nothing in any version, and the normal-tap test passed every time. Each fault was caught only by the test written for the situation that triggers it.

Noor stands beneath a tram handrail, gripping a hanging strap while reading a small phone, with a long braid over her shoulder and a crossbody bag at her hip.
A layout that passes in a test window still has to fit one hand, a small screen and the text size its owner chose.

Make done mean the checks ran

The demonstration suggests a rule of thumb for which checks a change needs:

  • Every change: flutter analyze and dart format --output=none --set-exit-if-changed ., with zero new issues.
  • New or changed logic: unit tests for the view model or repository, using fakes, as Flutter's architecture guide recommends.
  • Any await in a widget or view model: a widget test where the user leaves before the future completes.
  • Any layout change: a widget test at the narrowest phone width you support, with large text.
  • Navigation, plugins or platform code: an integration test, which Flutter's testing overview describes as running on a real device or emulator, plus a manual pass on at least one physical phone.

The agent can run most of this itself. Flutter now publishes agent plugins for Claude Code, Codex, Cursor and others, with skills for adding widget tests and fixing layout overflows. The Dart and Flutter MCP server, still marked experimental, lets an agent analyze files, run tests and read runtime errors from a running app. Its report is still a claim until you see the output; the article on verifying that an AI agent actually ran the checks it says passed covers that step.

Paqad does not make an agent better at Flutter and cannot test on your phone. For a Flutter project its stack detection reads pubspec.yaml and pubspec.lock, selects its flutter pack, whose declared test runner is flutter-test, and lists the Dart MCP server for that stack. It writes the names into each host tool's file and never installs or starts the server. Paqad's documentation shows which MCP servers each stack pack lists.

Put a stopwatch on the whole task

Whether any of this saves time is a measurement, not a feeling. In METR's early-2025 trial, experienced open-source developers took 19% longer with AI tools while believing afterwards that they had been sped up by 20% (METR). METR now marks those results as out of date and calls its later data an unreliable signal, which is a reason to measure your own work rather than borrow anyone's figure.

  1. Pick three or four comparable tasks of the kind you would hand over, such as a new screen with one network call.
  2. Write down your own estimate for doing each one by hand before you start.
  3. Log time in four parts: briefing, waiting and reviewing, fixing after the agent said it was done, and checking on a device.
  4. Note which check found each fault after "done": analyzer, a test, the device run, or a user.
  5. Compare the total with your estimate, not the minutes until the first working build.

If the fixing time keeps outgrowing the briefing you saved, shrink the slices or keep that kind of work by hand. If most faults surface on the device, add the test that would have caught them to the brief.

Mira holds up a stopwatch beside Ivo, who bends over at a finish line, while Noor crouches and points at his untied shoelace.
Time the whole lap, including the part after the agent says it is finished.

Frequently Asked Questions

Treat the agent's output as unfinished until the checks you named in advance have passed, and measure the whole task rather than the first build. When debugging eats the saving, look first for decisions the brief left open and the agent made for you; narrowing the brief is a cheaper first move than switching models.

Delegate what follows a pattern already in the codebase: layouts, models, repository calls and tests. Keep ownership of state, lifecycle and navigation decisions, and review those lines first in every diff. If you cannot explain where a piece of state lives after reading the change, ask the agent to move it into the existing view model before you accept anything else.

Name the files it may touch and check the diff against that list before reading the code. One screen, or one view model and its tests, per run is a sensible default. If the agent says it needs to change shared widgets or the router, stop and make that a separate, reviewed change.

Let the change decide: analyzer and format always, unit tests for logic, widget tests for every awaited call and layout change, and integration or device checks for navigation, plugins and platform code. Write the list into the brief so the agent can run it and you can see it was run.

Estimate the hand-built time before each task, log the agent-assisted time in parts, and compare after several tasks rather than one. Count faults found after the agent reported done, by the check that found them. That count tells you which test to add, and the totals tell you which kinds of work to keep handing over.