Opinionated rules, patterns, and best-practices for building secure, high-performance Flutter mobile applications with Dart.
You're building Flutter apps that need to scale, perform, and ship without breaking. But between state management chaos, performance bottlenecks, and security vulnerabilities, you're spending more time debugging than developing.
Most Flutter projects start simple but quickly become maintenance nightmares:
Sound familiar? You need a systematic approach that prevents these problems from day one.
These Cursor Rules establish a complete development framework that transforms how you build Flutter apps. Instead of learning painful lessons in production, you get battle-tested patterns that work at scale.
What You Get:
Instead of hunting down why your list is janky, these rules enforce ListView.builder for long lists and const constructors by default. Your Flutter DevTools timeline stays clean, and frame drops become rare exceptions.
// Before: Performance nightmare
Column(children: items.map((item) => ItemWidget(item)).toList())
// After: Smooth scrolling enforced
ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) => const ItemWidget(items[index]),
)
No more "we'll add security later" technical debt. These rules enforce encrypted storage, proper TLS configuration, and secret management from your first commit.
// Enforced pattern: Secure token storage
await FlutterSecureStorage().write(
key: 'auth_token',
value: token,
aOptions: AndroidOptions(
encryptedSharedPreferences: true,
),
);
Stop shipping broken features. These rules require 100% coverage for business logic and establish widget testing patterns that catch UI regressions before users do.
Whether you choose Riverpod or BLoC, these rules establish clear boundaries between UI, state, and business logic. Your features become testable, maintainable, and actually work as expected.
Before: You're adding a user profile feature. You mix business logic in widgets, forget to add loading states, and spend hours debugging why the UI isn't updating correctly.
After: Following these rules, you:
freezed for immutabilityTime saved: 3-4 hours per feature in debugging and rework.
Before: You realize in production that user tokens are stored in plain text and API calls aren't properly validated.
After: Security is built in:
--dart-define in CITime saved: Days of security retrofitting and potential breach response.
Before: Users complain about lag. You spend hours with Flutter DevTools finding rebuild hotspots and memory leaks.
After: Performance patterns are enforced:
Result: 60% fewer performance-related bugs and complaints.
.cursorrules file in your Flutter project rootflutter_lints, very_good_analysis, riverpod_generatorlib/
└─ src/
└─ features/
└─ [feature_name]/
├─ data/ # API clients, models
├─ domain/ # Business logic, use cases
└─ presentation/ # Widgets, screens
Ready to transform your Flutter development? These rules represent years of production lessons condensed into immediately applicable patterns. Your future self (and your users) will thank you for implementing them today.
Stop fighting Flutter's complexity. Master it.
## You are an expert in Flutter, Dart, DevSecOps, Riverpod, BLoC, Firebase, REST/GraphQL, GitHub Actions and fastlane.
---
## Key Principles
- Treat every widget as immutable; prefer `const` constructors and pure functions.
- Separate **UI**, **state management**, and **business logic** (MVVM or Clean Architecture).
- Build security in from day 0: zero-trust mindset, least-privilege, encrypted storage.
- Fail fast: detect problems at compile time with strong typing, linting and exhaustive `switch`.
- Automate everything: CI ➜ test ➜ static-analysis ➜ security-scan ➜ build ➜ deploy.
- Keep rebuild surfaces minimal; measure with Flutter DevTools and the Impeller frame timeline.
- One source of truth per state domain; no hidden mutable globals.
---
## Dart Language Rules
- **Null-safety** is mandatory; no `!` unless wrapped by `assert`.
- Use `late` only for DI; prefer top-level `final` or constructor injection.
- Use `static const` for compile-time constants, not `final`.
- Prefer **extension methods** over utils classes.
- Choose **records** and **patterns** for lightweight DTOs instead of maps.
- Use `=>` for single-expression functions; keep bodies <15 LOC.
- Exhaustive `switch`/`when` on sealed classes:
```dart
switch (state) {
case Loading(): …
case Data(:final list): …
case Error(:final message): …
}
```
- Directory layout: `lib/` → `src/` → `features/` → `data`, `domain`, `presentation`, `widgets`.
---
## Error Handling & Validation
- Guard clauses first; happy path last:
```dart
Future<User> fetchUser(int id) async {
if (id <= 0) return Future.error(InvalidId());
… // happy path
}
```
- Represent failures with `sealed` union types (`Success` | `Failure`). Avoid throwing inside business logic; propagate `Result<T,E>`.
- Convert only at the app layer to user-friendly `Snackbar` or dialog.
- Log with `dart:developer` or `logger` package; scrub PII before shipping.
---
## Flutter Framework Rules
- Stateless by default; use `StatefulWidget` only for local ephemeral state.
- Add `Key` to any list/grid item, page-level widget, or when order can change.
- Long lists: `ListView.builder`, `CustomScrollView` + `SliverList`, never unbounded `Column`.
- Navigation: pick one router (`go_router`, `autoroute`, or `beamer`) and use declarative URL-based routing.
- Theming: single `ThemeData` source, extend with `ThemeExtension` for custom tokens.
- Accessibility: `Semantics` labels, large-font checks, high-contrast palettes, tappable ≥ 48 px.
---
## State Management Rules
### Riverpod (preferred for new code)
- Use `riverpod_generator` + `freezed` to create **immutable** states.
- Group providers by feature and expose only the public provider.
- Compute-heavy providers → `autoDispose` + `keepAlive`.
- Never mutate state inside UI; call `ref.read(controllerProvider.notifier).action()`.
### BLoC (legacy / complex flows)
- One BLoC per aggregate root.
- Events in, States out; keep reducer pure.
- Use `hydrated_bloc` for offline persistence.
---
## Testing Guidelines
- 100 % logic functions covered by **unit tests** (`test` + `mocktail`).
- **Widget tests** for each screen; pump with `setSurfaceSize` for consistent goldens.
- **Integration tests** via `flutter_test` + `integration_test` and run on Firebase Test Lab.
- CI rule: tests must pass in <8 min; parallelize shards.
---
## Performance Optimization
- Activate Impeller (`flutter run --impeller`).
- Mark pure widgets `const`; DevTools “Rebuild Stats” should stay ≤ 1 per frame.
- Image handling: pick correct resolution assets; use `cached_network_image` with compression.
- Enable tree-shaking: no `dart:mirrors`, prefer `part` + code-gen over reflection.
- Run `flutter analyze --performance` in CI.
---
## Security Best Practices
- Store secrets in CI vault; inject via `--dart-define`.
- Use `flutter_secure_storage` for tokens, encrypted with keychain/keystore.
- Enforce TLS 1.2+, certificate pinning with `http/io_client` override.
- Strip logs in release (`–dart-define=LOG_LEVEL=none`).
- OWASP MASVS compliance checklist in PR template.
---
## CI/CD & DevOps
- Pipeline stages: `format ➜ analyze ➜ test ➜ security_scan ➜ build_apk/ipa ➜ upload_store`.
- Use `melos` for monorepo and `shorebird` for hot-patches.
- Deploy via fastlane: `fastlane ios beta`, `fastlane android beta`.
- Tag releases semver: `vX.Y.Z+build`. Generate changelog from PR titles.
---
## Lint & Tooling
- Enforce `package:flutter_lints` + `very_good_analysis`.
- IDE setup: enable “Format on Save” and “Organize Imports”.
- Static analysis gates PR merge.
---
## Common Pitfalls
- Missing `await` leading to unhandled `Future`.
- Forgetting `const` → extra rebuilds.
- Deeply nested builders; extract widgets.
- Over-using `setState` for global changes; move to Riverpod/BLoC.
---
## Example File Skeleton
```
lib/
└─ src/
└─ features/
└─ auth/
├─ data/
│ ├─ auth_api.dart
│ └─ models/
├─ domain/
│ └─ usecases/
└─ presentation/
├─ login_screen.dart
└─ widgets/
```
Adhere strictly to these rules to keep Flutter codebases robust, testable, and production-ready.