Stack conventions
Flutter (Dart)
Flutter apps in Dart with null safety, state managed through the repository's chosen approach, tested with unit, widget and integration tests.
Flutter apps in Dart with null safety, state managed through the repository's chosen approach, tested with unit, widget and integration tests.
Used for: flutter.
Principles
- Follow Effective Dart and keep flutter analyze clean under the repository's analysis_options.yaml (flutter_lints or stricter).
- Build UIs from small, immutable widgets with const constructors; prefer composing widgets over helper methods that return widgets.
- Keep business logic out of widgets; separate UI, state (view models / notifiers / blocs) and data (repositories, services).
- Use the state management approach the repository already uses (Provider/ChangeNotifier, Riverpod, Bloc, or plain ValueNotifier) consistently; do not introduce a second one.
- Embrace sound null safety - avoid the ! operator and late without a guarantee; model absence explicitly.
- Never do I/O or heavy computation in build(); build must be pure and cheap.
- Use async/await with proper error handling; check mounted (context.mounted) before using a BuildContext after an await.
- Dispose what you create (controllers, streams subscriptions, animation controllers, focus nodes).
- Design for accessibility and every screen size from the start (semantics, text scaling, adaptive layouts).
Conventions
Accessibility
- Give icons and images without text a semantic label (Semantics, semanticLabel, tooltip on IconButton).
- Support text scaling (do not hard-code text heights; test with large font scales) and minimum tap targets of 48x48 dp.
- Ensure sufficient colour contrast and do not convey meaning by colour alone.
- Verify with the accessibility guideline matchers in widget tests (meetsGuideline(textContrastGuideline), androidTapTargetGuideline, labeledTapTargetGuideline).
Concurrency
- Dart is single-threaded per isolate; use Futures and Streams for I/O and isolates for CPU-bound work.
- Cancel stream subscriptions and timers in dispose; guard setState and context use with mounted checks after awaits.
- Use unawaited() explicitly for fire-and-forget futures so the intent is visible (unawaited_futures lint).
Dependencies
- Pin versions with caret constraints in pubspec.yaml and commit pubspec.lock for applications.
- Prefer packages with verified publishers and good pub.dev scores; check platform support before adding.
- Run dart pub outdated regularly and upgrade deliberately.
Documentation
- Use /// doc comments starting with a single-sentence summary; document public APIs of packages.
- Use [square brackets] to reference identifiers in doc comments.
Errors
- Catch specific exceptions (on SocketException catch (e)); avoid catch-all clauses without rethrow or logging.
- Throw Exception subclasses for runtime failures; Error subclasses signal programmer bugs and should not be caught.
- Report uncaught errors via FlutterError.onError and PlatformDispatcher.instance.onError to the crash reporter.
- Show users friendly error states; never leave a Future's error unhandled.
Logging
- Use package:logging or dart:developer log(); no print in production code (avoid_print lint).
- Do not log tokens or personal data; strip verbose logging from release builds (kReleaseMode).
Naming
- UpperCamelCase for types and extensions; lowerCamelCase for members, variables and constants; lowercase_with_underscores for files, directories, packages and import prefixes.
- Prefix private members with _; name booleans as assertions (isLoading, hasError).
- Name widgets for what they show (OrderSummaryCard), not how (BlueBox).
Performance
- Use const constructors wherever possible so subtrees are not rebuilt.
- Use ListView.builder / GridView.builder / slivers for long lists; never build large lists eagerly.
- Avoid saveLayer-heavy widgets (Opacity with animation, ClipRRect over large areas) in animations; prefer AnimatedOpacity or FadeTransition.
- Move heavy computation off the UI isolate with Isolate.run or compute.
- Profile in profile mode with DevTools, not in debug mode.
Security
- Store tokens in flutter_secure_storage (Keychain/Keystore), never in SharedPreferences or source.
- Do not embed API secrets in the app binary; anything in the app can be extracted.
- Use HTTPS only and validate server data before use; obfuscate release builds (--obfuscate --split-debug-info) where appropriate.
- Never build SQL or JavaScript for a web view from user input; use sqflite/drift arguments and typed APIs (injection).
Structure
- Organise lib/ by feature (lib/ui/
/, lib/data/repositories/, lib/data/services/, lib/domain/models/) or follow the repository's existing layout. - Use package imports (package:app/...) consistently; avoid relative imports that cross lib/ boundaries.
- Keep main.dart minimal (dependency setup and runApp).
- Put generated files (*.g.dart, *.freezed.dart) where the generator expects them and do not edit them by hand.
Testing
- Unit tests for view models and repositories; widget tests (testWidgets, WidgetTester, find) for UI behaviour; integration_test for critical flows.
- Use fakes for repositories and services rather than mocking widgets; mocktail or mockito if the repository uses them.
- Use pumpWidget with the real providers replaced by fakes; pumpAndSettle only when animations must finish.
- Golden tests for visual regressions where the repository has them.
- Name test files
<thing>_test.dartand tests after the behaviour (testWidgets('shows error when login fails', ...)). - Done means tested: new or changed behaviour ships with tests in the same change, and a fixed bug gets a regression test that fails without the fix.
Review checklist
- Does flutter analyze report no new issues and is the code dart format clean?
- Are new widgets small, immutable and using const constructors where possible?
- Is business logic in view models or repositories rather than in widgets?
- Is the repository's existing state-management approach used consistently?
- Are controllers, subscriptions and focus nodes disposed?
- Is BuildContext use after await guarded by mounted checks?
- Is the ! operator avoided except where non-null is guaranteed?
- Are long lists built lazily (builder constructors or slivers)?
- Do interactive elements have semantics labels, adequate tap targets and support text scaling?
- Are secrets kept out of the app binary and tokens stored in secure storage?
- Are there unit and widget tests for new behaviour?
- Are new dependencies justified, pinned, and supported on all target platforms?
Quality checks (run when your repository has the tool configured)
| Check | Kind | Tool | Blocks the pull request |
|---|---|---|---|
flutter-analyze |
lint | flutter analyze | no |
dart-format |
format | dart format | no |
osv-scanner |
security | osv-scanner | no |
gitleaks |
security | gitleaks | no |