Flutter Standards
P0 — Design System Enforcement (CRITICAL)
Zero tolerance for hardcoded design values.
Before any UI work, identify the project's Theme Archetype by checking main.dart:
- Theme-Driven:
VThemeData(...).toThemeData()→ useTheme.of(context).textTheme - Token-Driven: Use static tokens (
VTypography.*) only when no global theme bridge exists or when defining the theme itself
Rules:
- Colors: Use tokens (
VColors.*,AppColors.*). NeverColor(0xFF...)orColors.red. - Spacing: Use tokens (
VSpacing.*). Never magic numbers like16or24. - Typography: Prefer
Theme.of(context).textTheme.*for adaptive UI. UseVTypography.*only for theme definitions. Never inlineTextStyle. - Borders: Use tokens (
VBorders.*). Never rawBorderRadius.circular(n). - Components: Use DLS widgets (
VButton) over raw Material widgets (ElevatedButton) when available.
Detail → refs/design-system.md
P0 — Error Handling (CRITICAL)
- Repositories return
Either<Failure, T>— no exceptions propagate to UI or BLoC. - Catch in Infrastructure only — convert
DioException/ external errors to typedLeft(Failure). - Fold in BLoC —
.fold(failure, success)to emit states. No try/catch in BLoC. - Typed failures —
@freezedunion failures (e.g.,UnauthorizedFailure). NeverLeft('string'). - Crashlytics routing — all
catchblocks route viaAppLogger.error(...)for observability. on Type catch— never barecatchwithouton.
Detail → refs/error-handling.md
P1 — Widgets (OPERATIONAL)
- State: Use
StatelessWidgetby default.StatefulWidgetonly for local state/controllers. - Composition: Extract UI into small, atomic
constwidgets. - Theming: Use
Theme.of(context). No hardcoded colors. - Layout: Use
Flex+Gap/SizedBox. - Widget Keys: All interactive elements must use keys from
widget_keys.dart. - File Size: If a UI file exceeds ~80 lines, extract sub-widgets into private classes.
- Specialized:
SelectionArea: For multi-widget text selection.InteractiveViewer: For zoom/pan.ListWheelScrollView: For pickers.IntrinsicWidth/Height: Avoid unless strictly required; for overlays preferStack + FractionallySizedBox.
- Large Lists: Always use
ListView.builder.
class AppButton extends StatelessWidget {
final String label;
final VoidCallback onPressed;
const AppButton({super.key, required this.label, required this.onPressed});
@override
Widget build(BuildContext context) =>
ElevatedButton(onPressed: onPressed, child: Text(label));
}
P1 — Idiomatic Flutter (OPERATIONAL)
- Async Gaps: Check
if (context.mounted)before usingBuildContextafterawait. - Composition: Extract complex UI into small widgets. Avoid deep nesting or large helper methods.
- Spacing:
- Prefer
spacingparameter onRow/Column(Flutter 3.10+) over inserting gaps between children. - Fallback: Use
Gap(n)orSizedBoxonly whenspacingcannot express the layout. - Empty UI: Use
const SizedBox.shrink(). - Simple gaps: Prefer
Gap(n)orSizedBoxoverPadding.
- Prefer
- Container: Use
ColoredBox/Padding/DecoratedBoxinstead ofContainerwhere possible. - Themes: Use extensions for
Theme.of(context)access. - No
_buildXxx()helpers: Extract toconst StatelessWidgetfor proper rebuild control.
P1 — Performance (OPERATIONAL)
- Rebuilds: Use
constwidgets andbuildWhen/selectfor granular updates. - Lists: Always use
ListView.builderfor item recycling. - Heavy Tasks: Use
compute()orIsolatesfor parsing/logic. - Repaints: Use
RepaintBoundaryfor complex animations. Debug withdebugRepaintRainbowEnabled. - Images: Use
CachedNetworkImage+memCacheWidth. UseprecachePicturefor SVGs. Always setmaxWidth/maxHeight. - Keys: Provide
ValueKeyfor list items and stable IDs. - Resource Cleanup: Dispose controllers/streams in
dispose(). - Pagination: Default to 20 items per page for network lists.
- Build Purity: Keep
build()free of heavy work; move logic to BLoC/Application.
BlocBuilder<UserBloc, UserState>(
buildWhen: (p, c) => p.id != c.id,
builder: (context, state) => Text(state.name),
)
Anti-Patterns
- No hardcoded colors/spacing:
Color(0xFF...),Colors.red,SizedBox(height: 10)are forbidden. - No inline
TextStyle: Usetheme.textTheme.*or design tokens. - No setState for server state: Server or shared state belongs in BLoC.
- No widget file over 80 lines without extraction.
- No inline Key strings: All keys must be constants defined in
widget_keys.dart. - No
_buildXxx()helper methods: Extract toconst StatelessWidgetprivate class. - No manual widget repetition: When 3+ sibling widgets differ only in data, map over a list.
- No BuildContext after await without mounted check.
- No root
setState(): UseBlocBuilderwithbuildWhenorcontext.select(). - No heavy work in
build(): Move sorting/filtering/heavy logic to BLoC orcompute(). - No non-
constleaf nodes: Applyconstto all static widgets. - No large
Columnlists: UseListView.builder. - No try/catch in BLoC: BLoC receives
Eitherand folds. - No plain string failures: Define typed
@freezedFailure subclasses. - No silent catch blocks: Always log and propagate.
- No direct controller access in widget: Use BLoC or Signals to decouple UI from state.
References
Load only what the current task requires:
- state-management — BLoC/Cubit, Riverpod, or GetX state; files matching
**_bloc.dart,**_cubit.dart,**_provider.dart,**_notifier.dart,**_controller.dart; keywords: Bloc, Cubit, Riverpod, GetX, Obx, AsyncValue, ref.watch - navigation — routing with go_router, auto_route, or GetX; files matching
**/*router*.dart,**/app_pages.dart,**/main.dart; keywords: GoRouter, AutoRoute, GetPage, Navigator, deep link, redirect - architecture — feature-based or layer-based clean architecture; files under
lib/features/**,lib/domain/**,lib/infrastructure/**,lib/application/**; keywords: feature, domain, infrastructure, DTO, mapper, Either - networking — Dio/Retrofit HTTP clients and interceptors; files under
**/data_sources/**,**/api/**; keywords: Dio, Retrofit, RestClient, Interceptor, token refresh - error-handling — Either/Failure detail and API error mapping; files under
lib/domain/**,lib/infrastructure/**; keywords: Either, fold, Left, Right, Failure, dartz - dependency-injection — get_it + injectable setup; files matching
**/injection.dart,**/locator.dart; keywords: GetIt, injectable, singleton, module - design-system — DLS token usage, modular and monolithic patterns; files under
**/theme/**,**/*_theme.dart,**/*_colors.dart; keywords: ThemeData, ColorScheme, AppColors, design token - localization — easy_localization with CSV/JSON; files under
**/translations/*.json,**/langs/*.csv; keywords: localization, tr(), easy_localization - notifications — FCM + flutter_local_notifications; files matching
**/*notification*.dart; keywords: FCM, FirebaseMessaging, push - security — OWASP Mobile, secure storage, pinning, obfuscation; files under
lib/infrastructure/**,pubspec.yaml; keywords: secure_storage, pinning, jailbreak, OWASP, PII - concurrency — isolates and compute() for heavy tasks; files matching
**/*isolate*.dart,**/*worker*.dart; keywords: Isolate, compute, ReceivePort, background - cicd — GitHub Actions and Fastlane pipelines; files under
.github/workflows/**.yml,fastlane/**; keywords: ci, cd, pipeline, deploy, workflow - testing — unit, widget, integration, robot pattern, mocking, bloc tests, widget keys; files under
**/test/**.dart,**/integration_test/**.dart,**/robots/**.dart; keywords: test, patrol, robot, blocTest, mocktail, WidgetKeys