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.
What You’ll Walk Away With
Section titled “What You’ll Walk Away With”- 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_runnerdrift, Gradle/CocoaPods, and golden-test flakiness
Scaffolding a Flutter App
Section titled “Scaffolding a Flutter App”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@riverpodsyntax (addriverpod_annotation,riverpod_generator,build_runner),go_routerfor navigation,diofor networking, andfreezedfor data classes. Use a feature-first folder layout (lib/features/<feature>/{data,domain,presentation}). Generate thebuild.yamland amelos-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.
From an empty directory:
claude "Scaffold a Flutter app with Material 3, Riverpod 3.x (code-generation \@riverpod syntax), go_router, dio, and freezed. Use a feature-first folder \layout and run build_runner once to verify codegen."Then keep the session warm: claude "now add a typed AppConfig from --dart-define-from-file with a dev and prod flavor".
Codex CLI prompts are positional. From an empty directory:
codex "Scaffold a Flutter app with Material 3, Riverpod 3.x using the \code-generation @riverpod syntax, go_router, dio, and freezed. Feature-first \folder layout. Run build_runner to verify generated files compile."For the longer codegen-and-verify loop, codex --full-auto lets it run build_runner and flutter analyze without prompting on each command, or hand it to Codex Cloud to scaffold and open a PR.
State with Code-Generation Riverpod
Section titled “State with Code-Generation Riverpod”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.
Navigation with go_router
Section titled “Navigation with go_router”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.
Networking with Dio
Section titled “Networking with Dio”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.”
Clean Architecture
Section titled “Clean Architecture”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.
Testing
Section titled “Testing”When This Breaks
Section titled “When This Breaks”The failure modes below are where Flutter + AI most often goes sideways:
build_runnerdrift. 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 cleanthen regenerate. Paste the exactbuild_runnererror into the AI and ask it to identify whether the cause is a missingpartdirective, a legacy/codegen syntax mix, or a version mismatch betweenriverpod_annotationandriverpod_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 updateor 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.dartthat loads a fixed font before tests run.
What’s Next
Section titled “What’s Next”- Expo Development Patterns if you are comparing cross-platform stacks
- React Native patterns for the JS-side comparison
- Backend integration for the API your
ApiServicetalks to