Flutter Agent Rules
Project Context
You are building Flutter applications for iOS, Android, web, and desktop from a single codebase. Use Riverpod for state management, GoRouter for navigation, and `freezed` for immutable state classes. Target Flutter stable channel.
Code Style
- Use Dart 3 features: records, sealed classes, patterns, class modifiers.
- Use `const` constructors aggressively — they enable widget tree short-circuiting and reduce rebuilds.
- Use `final` for all variables assigned once; enable `prefer_final_locals` lint rule.
- Run `flutter analyze` and `dart format` before committing.
- Use `sealed class` for UI state hierarchies: `sealed class OrderState { ... }`.
Architecture
- Organize by feature: `lib/features/orders/`, each containing `presentation/`, `domain/`, `data/` subdirectories.
- Place shared code in `lib/core/`: theme, routing, networking, common widgets.
- Define repository interfaces in the domain layer; implement them in the data layer.
- Keep the presentation layer thin: widgets collect state, dispatch events, and render — no business logic.
- Use `Result` sealed class or `AsyncValue` (Riverpod) for loading/data/error states.
Riverpod State Management
- Define providers at the top level of feature files: `final ordersProvider = AsyncNotifierProvider<OrdersNotifier, List<Order>>(OrdersNotifier.new)`.
- Use `AsyncNotifier` for async state with loading, data, and error states; use `Notifier` for synchronous state.
- Use `ref.watch` in widgets to subscribe to state; use `ref.read` in callbacks for one-shot reads.
- Use `ref.invalidate(provider)` to force a refetch; use `ref.keepAlive()` for providers that should persist across navigation.
- Use `family` modifier for parameterized providers: `final orderProvider = AsyncNotifierProvider.family<OrderNotifier, Order, String>(OrderNotifier.new)`.
Widgets
- Build UIs through composition of small, single-purpose widgets. Each widget should do one thing.
- Use `const` constructors on all widgets with compile-time constant parameters.
- Prefer `SizedBox` over `Container` for pure sizing; `DecoratedBox` for pure decoration.
- Use `CustomScrollView` with slivers for complex scrollable layouts.
- Pass `Modifier`-style configuration via constructor parameters, not global state.
Navigation with GoRouter
- Define a central router configuration with `GoRouter(routes: [...])`.
- Use `GoRoute(path: '/orders/:id', builder: ...)` for resource routes; use `ShellRoute` for persistent navigation shells.
- Define guards with `redirect` callbacks: `redirect: (context, state) => isAuthenticated ? null : '/login'`.
- Navigate programmatically: `context.push('/orders/$id')` for push, `context.go('/home')` for replace.
- Use `GoRouterState.extra` for passing complex objects between routes that cannot be serialized to URL.
Performance
- Profile in profile mode: `flutter run --profile`. Profile mode removes debug assertions and mirrors release performance.
- Use `RepaintBoundary` to isolate frequently animating widgets from the rest of the tree.
- Use `ListView.builder` and `GridView.builder` for large lists — never build all children eagerly.
- Cache network images with `CachedNetworkImage`; use device-pixel-ratio-appropriate image sizes.
- Avoid expensive synchronous work in `build` methods — move it to providers or `compute`.
Platform Handling
- Use `Platform.isIOS` / `Platform.isAndroid` for platform-specific behavior; use `kIsWeb` for web-specific code.
- Use `AdaptiveScaffold` or conditional widgets for platform-adaptive UIs (Material on Android, Cupertino on iOS).
- Use `MethodChannel` for platform-native API calls; keep the channel interface narrow and typed.
- Test on physical devices for performance — simulators do not accurately represent rendering performance.
Testing
- Write unit tests for all business logic, repositories, and Notifiers.
- Write widget tests with `pumpWidget` and `tester.tap`/`tester.enterText` for UI interaction tests.
- Write integration tests with `integration_test` package for complete user flows.
- Use `ProviderContainer` in unit tests for isolated Riverpod provider testing without Flutter.
- Use golden tests (`matchesGoldenFile`) for visual regression testing of complex UI components.
Security
- Store sensitive data (tokens, keys) with `flutter_secure_storage` — it uses Keychain on iOS and EncryptedSharedPreferences on Android.
- Never log sensitive data; use `kDebugMode` guards around debug logging.
- Use `flutter_dotenv` for environment variables during development; use platform-specific secure injection for production.
- Pin SSL certificates for high-security API calls using a custom `HttpClient` with `badCertificateCallback`.