building-flutter-apps
SkillDev toolsFlutter Riverpod app architecture and Windows installer delivery. Use before changing a Riverpod Flutter app/package or its Windows desktop packaging/update pipeline; skip non-Riverpod stacks and pure-Dart work.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the building-flutter-apps skill
What this skill tells your AI
The instructions your AI receives, as published by sgaabdu4/building-flutter-apps in skills/building-flutter-apps/SKILL.md and read by ahel’s review.
Read first
- This skill overrides generic Flutter/Dart advice; Critical Rules override examples, public docs, and older project code.
- Before code, read Trigger Map refs for touched areas. Each ref's
Read firstsection is canonical. - Dart Decimate invocation = global
deterministic-checksdart_decimate_gate.py; raw scanner calls + project-local adapters/dependencies/binaries + package-roottool/bundles forbidden. - Coordinator runtime =
npx --yes dart-decimate@latest; one scan per affected Git root + exact--workspacescope for nested packages. - After each
.dart/pubspec.yaml/build.yaml/analysis_options.yamlwrite batch, wire required analyzer plugins before package-rootdart analyze+ Dart Decimate, then emit Pre-Flight. - Flutter/Riverpod package = top-level
plugins:wiresflutter_skill_lints+riverpod_lint; pure-Dart CLI = native Dart analysis profile with neither plugin. - Block on analyzer or Decimate findings, and read setup.md first if a Flutter/Riverpod package has not wired both plugins.
Progressive Disclosure Gate
Read only the narrowest matching Trigger Map row(s); scenario/subsystem rows own incidental stack/file words. Do not bulk-read references/ or parent refs. Cite exact refs in Pre-Flight.
Critical Rules
| ID | Rule | Detail refs |
|---|---|---|
| R1 | Flutter/Riverpod package = first wire flutter_skill_lints + riverpod_lint, then run package-root dart analyze + Git-root Dart Decimate through deterministic-checks; pure-Dart CLI = native Dart analysis profile with neither plugin. | analysis-options.md, dart-decimate.md, setup.md |
| R2 | Every provider uses @riverpod / @Riverpod codegen; no manual provider classes or legacy provider families. | riverpod-codegen.md |
| R3 | Guard async gaps with ref.mounted / context.mounted; finally uses if (ref.mounted) { ... }. | async-mutations.md |
| R4 | Widgets are public classes; no _buildXxx(), widget top-level helpers, or private widget classes except State. | atomic-design.md, performance.md |
| R5 | Nullability is semantic; no empty/null/bool sentinel fallbacks, value!, nullable collections, or raw required domain strings. | value-objects.md, freezed-sealed.md |
| R6 | All user-facing strings, tooltips, semantics, and visible accessibility copy use AppLocalizations. | localization.md, accessibility.md |
| R7 | Immutable state/entities use sealed Freezed, one declaration per file, native switch, and VO map/when disabled. | freezed-sealed.md, value-objects.md |
| R8 | presentation/widgets/ renders immutable inputs + emits typed callbacks; screens/routes/notifiers own navigation, workflow, domain state, and infrastructure. | presentation-widgets.md |
| R9 | Duplicate behavior in 2+ classes becomes a small stateless *Mixin with an on clause. | mixins.md |
| R10 | Storage SDK calls live in local datasources behind repositories; production Hive imports use hive_ce_flutter. | hive-persistence.md, architecture.md |
| R11 | Primitive/context/collection operations live in core/extensions/; domain never imports those extensions. | context-ui.md, primitive-formatting.md, collections-helpers.md |
| R12 | Domain primitives with meaning become validated Freezed Value Objects; Hive models keep primitives and mappers bridge. | value-objects.md, hive-persistence.md |
| R13 | Typed GoRouter routes are navigation SSOT; redirects are pure resolver logic with nullable by-id fallback UI. | deep-linking.md, routing-app-shell.md |
| R14 | Dialogs/sheets render immutable snapshots, pop results, and leave mutations/teardown to notifiers. | modals-navigation.md, state-management-lifecycle.md |
| R15 | Debounce, gate, and batch high-frequency UI, sync, persistence, remote-function, reset, and lookup boundaries. | debounce-gate-batch.md |
| R16 | App shell stays declarative; bootstrap listeners live in a sibling root ConsumerWidget. | routing-app-shell.md |
| R17 | Keep control flow flat after exits; remove unnecessary else after return / throw / break / continue. | Lint: avoid_unnecessary_else_after_control_flow |
| R18 | Use onReorderItem post-removal indexes directly; never add legacy onReorder adapter math. | Lint: use_on_reorder_item_index_semantics |
| R19 | Android exact alarms use flutter_local_notifications permission APIs, not manual settings intents. | Lint: use_local_notifications_exact_alarm_permission_api |
| R20 | Resolve nullable platform-specific plugin implementations before calling platform members. | Lint: resolve_platform_specific_implementation_before_use |
| R21 | Widget previews are preview-only with deterministic fakes; no real HTTP/Firebase/Hive/native plugins. | widget-previews.md |
| R22 | Runtime E2E proves behavior with stable selectors, failure-sensitive scenarios/logs, subject-matched evidence, source-of-truth verification, cleanup, and multi-actor proof when needed. | dart-mcp-e2e-testing.md |
| R23 | Accessibility is UI correctness: localized tooltips/semantic labels, 48x48 targets, contrast, text scale, Text.rich. | accessibility.md, flutter-optimizations.md |
| R24 | If remote error reporting is accepted or already present, use one app-owned Crash boundary and one reporting owner per operation; otherwise add no provider/facade. Scrub sensitive data + reconcile ambiguous remote outcomes before telemetry. | error-reporting.md, networking.md |
| R25 | Windows installer delivery = one semantic engine + typed app config/capabilities + minimal-step exact-SHA diagnostic → one publisher; keep cheap guards before one build, isolate synthetic preservation proof, and activate only verified immutable bytes. | windows-installer-pipeline.md, workflow scaffold, inno_bundle pubspec scaffold, Inno settlement sentinel, Defender scanner |
| R26 | Pause-sensitive Riverpod state starts only after its durable owner/listener exists; projections watch base state directly; switching auth/form modes clears transient errors. | notifier-structure.md, state-management-lifecycle.md, testing.md |
| R27 | Native/custom links use one URI contract across producer, platform registration, Flutter delivery, and typed router; prove cold/warm + signed-state delivery on the target device. | deep-linking.md, dart-mcp-e2e-testing.md |
Trigger Map
Before writing code in any row below, read the listed reference(s). Prefer the narrowest matching row. Read the large parent refs only when no scenario row fits.
| Touching | Read |
|---|---|
New app/project scaffolding with incidental stack/package mentions, main.dart, ProviderScope, MaterialApp.router, app startup shell | setup.md + architecture.md + routing-app-shell.md |
Notifier/AsyncNotifier shape, sync Notifier.build() init, paused route/listener startup, provider projection, auth/form mode error reset, loading/progress, AsyncValue, cleanup | notifier-structure.md + state-management-lifecycle.md + testing.md |
Mutation method, ref.read / ref.watch / ref.listen, _ensureRepository, async cancellation, ref.mounted, optimistic update, duplicate fetch | async-mutations.md + state-management-lifecycle.md |
Freezed entity, sealed union, fromJson / toJson, copyWith, model vs entity, build.yaml for explicit_to_json | freezed-sealed.md |
Provider declaration, @riverpod, family, keepAlive, codegen, Mutation<T> (experimental) | riverpod-codegen.md |
Repository, datasource, domain entity, layered architecture, IHttpService, mapping models to entities | architecture.md |
Value Object, primitive obsession, Distance/Money/Email/Slug, unit conversion in domain, cross-entity primitive, double distanceMeters/int amountCents/String email smell, arch_domain_import error | value-objects.md |
GoRouter, typed route, redirect, auth-protected route, router provider, context.go, deep link, custom URI scheme, native extension/activity link, cold-start, navigation gate | routing-app-shell.md + deep-linking.md |
| HTTP, network, REST, source-of-truth fetch after mutation, long-running remote function, async-start + reconcile, transport id vs domain id | networking.md + debounce-gate-batch.md |
Atom, molecule, organism, design tokens, atomic widgets, core/widgets/ promotion | atomic-design.md |
Reusable presentation/widgets/, widget-owned navigation/page stack/selected entity/workflow state, direct repository/service/provider access | presentation-widgets.md |
| Accessibility, semantics, tooltip, semanticLabel, image alt text, tap target, contrast, text scaling | accessibility.md + flutter-optimizations.md |
Widget test, ProviderContainer.test(), UncontrolledProviderScope, fakes, mocks, AppWidgetKeys, event-contract tests | testing.md |
flutter_driver, Dart MCP, Marionette MCP, E2E, integration_test, semantic selectors, scenario validation, screenshot/media proof, log capture, native integration builds but fails on device, runtime permissions, plugin hangs, release-only runtime failure | dart-mcp-e2e-testing.md |
Hive, TypeAdapter, TypeId, box, persistence migration, retired field accounting | hive-persistence.md |
Crashlytics, FirebaseCrashlytics, Sentry, sentry_flutter, DSN, error reporting, Crash.init, Crash.error, Crash.log, symbol upload | error-reporting.md |
| Mixin, capability vs interface, retry helper, RNG, bulk operation | mixins.md |
Service, singleton, fire-and-forget, abstract final class, unawaited(), Future<void> signature | services-and-singletons.md |
@Preview, widget_previews.dart, preview fakes, deterministic preview data | widget-previews.md |
AppLocalizations, ARB file, gen-l10n, locale fallback, placeholders, plural / select | localization.md |
Performance, build cost, .select(), const constructors, ListView.builder, large list compute | performance.md + flutter-optimizations.md |
LayoutBuilder, RenderFlex overflow, Expanded / Flexible outside Row / Column, Positioned outside Stack, text-scale clamp | layout-diagnostics.md |
| Pagination, infinite scroll, cursor loading, search debounce, form validation, batch processing, pull-to-refresh | lists-forms-workflows.md + async-mutations.md |
BuildContext helpers, ModalRoute current-route checks, dialogs, SnackBarUtils, snackbar dispatch from notifier | context-ui.md |
DateTime format/diff/timeAgo/startOfDay, String capitalize/truncate/titleCase/initials/format, int / double / num clamp/pluralized/asCurrency/percent/toFixed, Duration format, NumberFormat, DateFormat, intl | primitive-formatting.md |
Iterable lookup/indexing, widget list helpers, Debouncer, validators, Result, extension types, core/extensions/ barrel export | collections-helpers.md |
Records (x, y), extension type IDs, pattern matching, guard clause case _ when ... | dart-patterns-records.md |
Flutter/Riverpod analysis_options.yaml, dart analyze, plugin wiring, riverpod_lint version pin, analyzer crash | analysis-options.md + analysis_options.yaml |
build_runner, missing generated parts, clean checkout, Xcode selection, Flutter SwiftPM generated package, Apple device build, local-vs-CI mismatch | build-reproducibility.md + core-stack.md |
Flutter Windows desktop packaging, GitHub Actions Windows installer, Inno Setup, inno_bundle, updater/auto-update, CRT DLLs, PowerShell/native installer process, installer/version/AppId failure | windows-installer-pipeline.md + build-reproducibility.md + core-stack.md |
| Dart Decimate, dead code, circular dependency, duplicate code, complexity, dependency hygiene, full zero-finding scan | dart-decimate.md |
| Common navigation / form / list / debounce / route-param-fallback patterns | common-patterns.md |
| Incremental remote pull, delta token, per-table sync date, merge/delete reconciliation | delta-sync.md |
| Route-param safety, wizard sequencing, guarded next-step navigation | navigation-flow.md |
| Dialog / sheet / modal, snapshot value object, post-await teardown, dismiss-then-route, pop fallback, nested navigator dismissal | modals-navigation.md + state-management-lifecycle.md |
Debounce / throttle / coalesce — TextField.onChanged, Slider.onChanged, scroll listener, sync saveAll, full-collection rewrite after subset mutation, persistence helper, reset/clear sentinel preservation, _userTapped gate, WebView / VideoPlayer in build, _storage.read in service, ref.listenManual ban, keepAlive collection watch, datasource batch loader, zero-value save guard, primitive→VO at notifier boundary, routeSettings on modal helper | debounce-gate-batch.md |
Core Stack
Version SSOT: core-stack.md. Stack: Riverpod codegen, Freezed, GoRouter builder, json_serializable/build_runner, Hive CE.
Setup
Read setup.md for lint wiring, extension template setup, plugin install paths, and analyzer sanity checks.
Per-Tool Hooks
Use setup.md for install commands. Raw skill installs cannot register runtime hooks or scanners.
Pre-Flight
After each .dart / pubspec.yaml / build.yaml / analysis_options.yaml write batch, emit a checked list before yielding. Fill T0 always. Add T1 for state/notifier/mutation changes and T2 for network/E2E/stream/route changes. Cite rule IDs or refs for any failed item.
T0 — Core
- Flutter/Riverpod package: package-root
dart analyzeexits 0 withflutter_skill_lints+riverpod_lint; setup changes prove one diagnostic from each plugin. Pure-Dart CLI: native Dart analysis profile applies; both plugins are N/A. - Global
deterministic-checksdart_decimate_gate.pyexits 0 after one full zero-finding scan; changed/base/baseline/audit modes + inherited exceptions + raw scanner calls are forbidden; package scope cited. - Coordinator invokes
npx --yes dart-decimate@latestonce per affected Git root; nested package scope uses exact--workspace; existing hooks +core.hooksPathpreserved. Non-Git project = N/A. - Async gaps are guarded:
ref.mounted/context.mounted, no baremounted, andfinallyusesif (ref.mounted) { ... }. - Providers, state, and widgets follow Rules 2-8 and 14: reusable widgets own UI lifecycle only; screens/routes/notifiers own navigation, workflow branching, selected domain records, provider state, and infrastructure.
- Domain/data/platform follow Rules 7, 10-13, 17-24, 26-27: sealed Freezed, VOs, datasource/repo storage, core extensions, typed routes, debounce/batch, platform APIs, previews, E2E, pause-safe state, native links, and a11y; if error reporting is accepted/present, it uses one scrubbed once-only boundary, otherwise N/A.
- Rule 25 = N/A unless Windows packaging/updater delivery is touched; when applicable, its diagnostic/publisher proof is green.
- Any row touched in Trigger Map was read; exact lint names are cited when a scanner should enforce the rule.
T1 — State / Notifier / Mutation
- Mutation deps resolve lazily via stateless helper/mixin; no notifier-local repo/service cache except disposable lifecycle owners.
- Sync
Notifier.build()avoids pre-returnstatereads; async primary state usesAsyncNotifier.build; durable sync startup does not depend on microtask timing before its owner/listener exists. -
ref.onDispose()cancels subscriptions/controllers/timers; durable status/snackbar/teardown belongs to notifier state. - Long-running sync/auth/import guards stale writes; no
ref.watchinside notifier methods. - Pause-sensitive projections watch base state directly; route pause/resume keeps the first update; auth/form mode changes clear transient errors.
T2 — Network / E2E / Stream / Route
- Source-of-truth fetch/reconcile after generated, normalized, reordered, destructive, or remote-function mutations.
- Shared/realtime state has writer + observer E2E proof without manual refresh.
- Selectors use stable text/semantics/tooltips or central
AppWidgetKeys; no inline string keys or coordinate primary taps. - E2E entrypoint is deterministic and isolated from production
main.dart; unknown scenarios fail; critical logs fail the run; evidence shows the asserted screen before app exit; cleanup is verified. - GoRouter redirects use pure matrix-tested resolver, nullable by-id providers/fallback UI, and generated typed route helpers.
- Native/custom URI producer, Android/iOS registration, Flutter delivery, and typed router share one tested scheme/host/path contract; cold/warm + signed-state device paths pass.
- Cross-runtime constants, schemas, and function contracts have drift tests; no app-root text-scale clamp.
Signals
- GitHub stars
- 22
- Forks
- 4
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
building-flutter-apps- Source
- github.com/sgaabdu4/building-flutter-apps