Skip to content

Flutter Development Patterns

You ask the AI for a Riverpod provider, it gives you StateNotifierProvider with the legacy syntax, your codebase uses the code-generation @riverpod annotation, and now build_runner is throwing conflicting-generated-file errors. Or the AI writes a widget that rebuilds the entire tree on every keystroke and the list janks on a mid-range Android. Flutter’s problem is rarely “can the AI write Dart” - it is that Dart has three valid ways to do everything, and the AI keeps mixing them.

This recipe shows the Flutter workflows that produce code matching your existing architecture, across Cursor, Claude Code, and Codex.

  • A scaffolding prompt that pins your state, routing, and networking choices so the AI stops mixing patterns
  • A copy-paste prompt for a code-generation Riverpod (@riverpod) async provider that handles caching and refresh
  • A go_router prompt with an auth redirect that does not loop
  • A Dio API-service prompt with interceptors, retry, and typed models
  • A “When This Breaks” map of the failures that actually cost time: build_runner drift, Gradle/CocoaPods, and golden-test flakiness

Flutter’s defaults change between major versions, so state the stack explicitly. As of June 2026 the current major line is Riverpod 3.x with the code-generation @riverpod syntax, go_router for navigation, and dio for networking.

Open Cursor in an empty folder, enter Agent mode (Cmd/Ctrl + I), and paste:

Scaffold a new Flutter app with flutter create. Set up Material 3, Riverpod 3.x using the code-generation @riverpod syntax (add riverpod_annotation, riverpod_generator, build_runner), go_router for navigation, dio for networking, and freezed for data classes. Use a feature-first folder layout (lib/features/<feature>/{data,domain,presentation}). Generate the build.yaml and a melos-free single-package setup.

Agent mode runs flutter create, edits pubspec.yaml, and runs dart run build_runner build. Review the dependency versions it pins.

The payoff of the @riverpod annotation is that async loading, caching, and family parameters become declarative. Ask for that explicitly.

How to evaluate the output: it must include the part '*.g.dart'; directive and use the unified Ref (Riverpod 3.x dropped the per-provider generated refs like UserProfileRef), not the old AutoDisposeFutureProviderRef. If you see StateNotifier anywhere, it reverted to the legacy style - send it back. Run dart run build_runner build --delete-conflicting-outputs and confirm it compiles before trusting it.

The go_router auth redirect is the classic infinite-loop bug: redirect unauthenticated users to /login, but forget to allow /login itself through the guard, and the router bounces forever.

Check that the redirect returns null for the auth routes themselves - that single guard clause is what prevents the loop, and it is the line the AI most often omits.

Evaluate it on error handling: the happy path is trivial, but the 401-refresh-and-replay and the typed ApiFailure are what make it production code. If the AI just rethrows DioException, push back with “map every DioException to the sealed ApiFailure.”

For larger apps, the layering prompt is worth getting right once. Give the AI a concrete feature, not an abstract “set up clean architecture.”

The constraint that matters: the domain layer must not import Flutter or dio. Ask the AI to confirm that, then spot-check the imports - a leaked package:flutter/material.dart in a use case is the tell that the layering is cosmetic.

The failure modes below are where Flutter + AI most often goes sideways:

  • build_runner drift. Symptoms: “conflicting outputs,” stale generated code, or a provider that “does not exist.” Fix: dart run build_runner build --delete-conflicting-outputs, and if it still fights you, flutter clean then regenerate. Paste the exact build_runner error into the AI and ask it to identify whether the cause is a missing part directive, a legacy/codegen syntax mix, or a version mismatch between riverpod_annotation and riverpod_generator.
  • Gradle and CocoaPods failures. Android build failures after a dependency bump are usually a Gradle/AGP or Kotlin version mismatch; iOS failures are usually a Pod that needs pod repo update or a higher iOS deployment target. Paste the full build log and ask the AI to pin the specific versions - do not let it bump the Flutter SDK to “fix” a Pod issue.
  • Golden-test flakiness in CI. Goldens rendered on macOS locally will not match a Linux CI runner pixel-for-pixel because of font rendering. Generate and verify goldens on one OS, or load a bundled test font. If goldens flake, ask the AI to add flutter_test_config.dart that loads a fixed font before tests run.