Skip to content
Back to skills

Flutter

ASecurity

Builds, debugs, and ships Flutter apps: widgets, layout constraints, state management, navigation, performance, and store releases. Use when writing Dart widgets or screens; when a RenderFlex overflows, a viewport is given unbounded height, or a RenderBox was not laid out; when setState is called during build or after dispose, or BuildContext is used across an async gap; when the UI janks, scrolling stutters, or images exhaust memory; when hot reload stops applying changes; when Provider, Riv...

  • 17 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 6, 2026
designgoswiftkotlinreactnodeexpresstestingdebuggingbackendperformance

Works with

  • cli

Security analysis

A100/100

Pro scans all 20 files and shows the line behind each finding

Scanned September 6, 2026

npx -y skills add clawic/skills --skill flutter --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Flutter?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Flutter
[![Security: A โ€” Skills Directory](https://www.skillsdirectory.com/api/skills/clawic-flutter/badge)](https://www.skillsdirectory.com/skills/clawic-flutter)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: Flutter
slug: flutter
version: 1.0.3
description: 'Builds, debugs, and ships Flutter apps: widgets, layout constraints, state management, navigation, performance, and store releases. Use when writing Dart widgets or screens; when a RenderFlex overflows, a viewport is given unbounded height, or a RenderBox was not laid out; when setState is called during build or after dispose, or BuildContext is used across an async gap; when the UI janks, scrolling stutters, or images exhaust memory; when hot reload stops applying changes; when Provider, Riverpod, or Bloc rebuild too much or too little; when routing with Navigator or go_router, deep links, or back-button handling; when the keyboard covers a form field or validation misfires; when a platform channel throws MissingPluginException; when localizing strings or mirroring a layout for right-to-left; when widget, golden, or integration tests hang or fail in CI; or when a build works in debug but the release APK, IPA, or web bundle breaks. Not for React Native or native-only Swift and Kotlin work.'
homepage: https://clawic.com/skills/flutter
changelog: Display name shown correctly
metadata:
  clawdbot:
    emoji: ๐Ÿฆ
    requires:
      bins:
      - flutter
    os:
    - linux
    - darwin
    - win32
    displayName: Flutter
    configPaths:
    - ~/Clawic/data/flutter/
    - ~/flutter/
    - ~/clawic/flutter/
  openclaw:
    requires:
      config:
      - ~/Clawic/data/flutter/
      - ~/flutter/
      - ~/clawic/flutter/
---

User preferences live in `~/Clawic/data/flutter/config.yaml` (see Configuration); nothing else is stored on the user's machine. If you have data at an old location (`~/flutter/` or `~/clawic/flutter/`), move it to `~/Clawic/data/flutter/`, and say in one line that you moved it and from where.

## When To Use

- Writing or reviewing Flutter UI: widgets, layouts, screens, lists, forms, animations, theming, custom painting
- Decoding a framework exception: overflow, unbounded constraints, setState during build or after dispose, deactivated ancestor
- Structuring an app: state management choice, routing, repositories, dependency injection, folder layout
- Performance work: jank, dropped frames, slow scrolling, memory growth, oversized images, startup time
- Reaching more users: translations, plurals, right-to-left layout, text scaling, semantics, tablet and desktop windows
- Shipping: build modes, flavors, signing, obfuscation, size analysis, store artifacts, release-only failures
- Reaching native: platform channels, FFI, plugins, permissions, background execution, per-platform behavior
- Not for React Native (`react-native`), native-only Swift (`swift`) or Kotlin (`kotlin`) work, or cross-framework motion systems (`animate`) โ€” this is Flutter and Dart

## Quick Reference

| Situation | Play |
|---|---|
| "A RenderFlex overflowed by N pixels" | A child asked for more than the parent allows โ€” `Expanded`/`Flexible` for a flex child, or make the axis scrollable โ†’ `layout.md` |
| "Vertical viewport was given unbounded height" | A scrollable inside a `Column` or another scrollable โ€” `Expanded`, a fixed height, or slivers; `shrinkWrap: true` is the slow escape โ†’ `layout.md` |
| "RenderBox was not laid out" | Almost always a knock-on: scroll UP to the FIRST exception in the console and fix that one โ†’ `debug.md` |
| "setState() or markNeedsBuild() called during build" | A notifier fired, or navigation/snackbar ran, inside `build` โ€” move it to a callback or a post-frame callback โ†’ `debug.md` |
| "setState() called after dispose()" | An `await` outlived the widget โ€” `if (!mounted) return;` after every await (rule 2) โ†’ `async.md` |
| "Looking up a deactivated widget's ancestor" | A captured `BuildContext` used after its widget left the tree โ€” capture the `NavigatorState` or `ScaffoldMessengerState` BEFORE the await โ†’ `async.md` |
| "Incorrect use of ParentDataWidget" | `Expanded`/`Flexible`/`Positioned` is not a direct child of `Row`/`Column`/`Flex`/`Stack` โ†’ `layout.md` |
| List items keep the wrong state after reorder or delete | Missing or unstable keys โ€” `ValueKey(item.id)`, never `UniqueKey()` in a builder (rule 4) โ†’ `state.md` |
| A widget rebuilds far more than it should | Climb the Rebuild Scope Ladder below, then confirm in DevTools' rebuild counter โ†’ `performance.md` |
| Scrolling stutters, frames dropped | Profile mode on a real device first, then image decode size, `saveLayer`, and per-item work โ†’ `performance.md` |
| Memory climbs while scrolling images | Decoded bytes = width ร— height ร— 4, independent of file size โ€” set `cacheWidth`/`cacheHeight` โ†’ `performance.md` |
| Choosing or migrating a state management library | Match the repo (`state_management: auto`); greenfield defaults and migration cost โ†’ `architecture.md` |
| Route needs an argument, a result, or a deep link | Typed routes and result handling with Navigator or go_router โ†’ `navigation.md` |
| Keyboard covers the field, focus jumps, validation fires too early | Controller lifetime, `AutovalidateMode.onUserInteraction`, scroll-on-focus โ†’ `forms.md` |
| Parsing, caching, or persisting data | Isolate for large payloads, local DB as the source of truth for offline โ†’ `data.md` |
| An animation stutters, leaks, or never restarts | Controller lifetime, `child:` hoisting, implicit vs explicit choice โ†’ `animations.md` |
| `MissingPluginException` right after adding a plugin | Hot restart does not register plugins โ€” full stop and re-run โ†’ `platform.md` |
| Right on the phone, broken on tablet, web, or desktop | Window size classes, `LayoutBuilder` vs `MediaQuery`, platform-adaptive widgets โ†’ `adaptive.md` |
| Text overflows once the user enlarges the system font | Text scaling is a layout input, not a cosmetic setting โ†’ `accessibility.md` |
| A chart, gauge, signature pad, or anything no widget can express | `CustomPaint`, and the `shouldRepaint` contract that keeps it cheap โ†’ `custom-painting.md` |
| Translated text overflows, dates look wrong, or the layout must mirror for Arabic | ARB workflow, ICU plurals, and the directional widgets โ†’ `localization.md` |
| A test hangs, or passes locally and fails in CI | `pumpAndSettle` against an endless animation; goldens are platform-specific โ†’ `testing.md` |
| Works in debug, broken in the release build | AOT, tree-shaken icons, stripped asserts, obfuscation, undeclared assets โ†’ `release.md` |
| `pub get` cannot resolve, or a plugin breaks after an upgrade | Version solving, overrides, transitive plugin constraints โ†’ `dependencies.md` |
| A Dart language question (records, patterns, `late`, `==`) | โ†’ `dart.md` |
| Anything else | Read the FIRST exception in full including "The relevant error-causing widget", reproduce it in a widget test, then open the file the message names โ†’ `debug.md` |

Depth on demand: `layout.md` constraints, flex, overflow, slivers ยท `state.md` lifecycle, keys, state preservation ยท `architecture.md` state management, DI, layering ยท `widgets.md` composition, context, build discipline ยท `async.md` futures, streams, isolates, cancellation ยท `navigation.md` Navigator, go_router, deep links ยท `forms.md` text input, focus, validation ยท `data.md` JSON, HTTP, persistence, offline ยท `performance.md` jank triage, rebuilds, images ยท `animations.md` implicit, explicit, transitions ยท `custom-painting.md` canvas, painters, custom render objects ยท `platform.md` channels, FFI, plugins, permissions ยท `adaptive.md` phone, tablet, web, desktop ยท `accessibility.md` semantics, text scaling, tap targets ยท `localization.md` translations, plurals, RTL, formats ยท `testing.md` widget, golden, integration ยท `release.md` modes, flavors, signing, size ยท `debug.md` symptom to cause ยท `dependencies.md` pub, versions, codegen ยท `dart.md` the language ยท `commands.md` CLI toolkit.

## Core Rules

1. **Constraints go down, sizes go up, the parent sets position.** Every layout exception decodes from this one sentence. A widget may only choose a size inside the constraints its parent passed; given unbounded constraints (a `Column`'s main axis, a scrollable's scroll axis) a child that wants "as much as possible" has nothing to resolve and throws. Worked example: `ListView` inside `Column` โ†’ wrap it in `Expanded`, which converts the unbounded height into the leftover bounded height; `SizedBox(height: 240)` also works; `shrinkWrap: true` compiles and then lays out every child on every scroll frame.
2. **`mounted` is the gate on every async gap.** After each `await`, before touching `setState`, `context`, or any controller: `final data = await repo.load(); if (!mounted) return; setState(() => _data = data);`. The `use_build_context_synchronously` lint catches the context case only โ€” controllers, tickers, and `ScaffoldMessenger` calls need the same guard and nothing warns you.
3. **Every disposable you create gets a matching line in `dispose`, in reverse creation order.** Controllers (`TextEditingController`, `ScrollController`, `AnimationController`, `PageController`, `TabController`), `FocusNode`s, `StreamSubscription`s, `Timer`s, `ValueNotifier`s. The leak is invisible in debug and surfaces as a climbing memory graph plus callbacks firing on dead widgets. Cheap check: a widget test that pumps the widget, pumps an empty tree, and asserts no exception (`testing.md`).
4. **Keys decide identity; without a key, position does.** Flutter matches a new widget to an existing `Element` by `runtimeType` + `key` at the same position, so inserting, removing, or reordering stateful children without keys hands state to the wrong item. Use `ValueKey(item.id)`. Never `UniqueKey()` inside a builder: a fresh key every build destroys and recreates the subtree โ€” including its scroll offset and running animations โ€” on every frame.
5. **`const` is the rebuild firewall.** Identical `const` widget expressions are canonicalized to a single instance, so `Element.update` sees `identical(oldWidget, newWidget)` and skips that subtree entirely. This is why extracting a static subtree into a `const` widget beats trying to "scope" a `setState` that still sits above it. A widget cannot be `const` if anything inside it reads `context` or a runtime value โ€” that, not style, is the real constraint.
6. **Frame budget = 1000 / refresh rate ms โ€” 16.7 ms at 60 Hz, 8.3 ms at 120 Hz โ€” shared by the UI thread (build, layout, paint) and the raster thread (GPU).** Any single synchronous unit of work that can exceed it (decoding a large JSON payload, image processing, crypto, sorting tens of thousands of items) belongs in an isolate (`async.md`). Wrapping it in a `Future` changes nothing: the work still runs on the same isolate and still blocks the frame.
7. **Only profile mode produces real numbers.** Debug builds run the JIT with assertions, service extensions, and widget-inspector instrumentation; frame timings there are noise. `flutter run --profile` on a physical device โ€” simulators and emulators have GPU behavior the shipped app never sees.
8. **Side effects never run inside `build`.** Navigation, snackbars, dialogs, notifier writes, network calls: each marks something dirty while the tree is building, which is exactly the "setState() or markNeedsBuild() called during build" exception. Put them in an event callback, a listener (`ref.listen`, `BlocListener`, `addListener`), or `WidgetsBinding.instance.addPostFrameCallback` when no other hook exists.
9. **A feature is not done until its release artifact runs.** AOT compilation, icon tree-shaking, stripped `assert`s, obfuscation, and asset declarations change behavior only in release. Build and launch the `--release` artifact on a device before calling it finished (`release.md`).

## Layout Error Decoder

Flutter's layout exceptions name the mechanism, not the mistake. The first move below is right in most cases; `layout.md` carries the reasoning.

| Message | What it actually means | First move |
|---|---|---|
| `A RenderFlex overflowed by N pixels` | A flex child's chosen size exceeds the space left after its fixed siblings | `Expanded`/`Flexible` on the growing child, or make the axis scrollable |
| `Vertical viewport was given unbounded height` | A scrollable sits inside another scrollable or a `Column`'s main axis | `Expanded`, a fixed height, or convert the parent to `CustomScrollView` + slivers |
| `BoxConstraints forces an infinite width/height` | An unbounded constraint reached a widget that expands (`double.infinity` under a `Row`, `Expanded` inside a scroll axis) | Bound it at the nearest parent that knows the real size |
| `RenderBox was not laid out` | A previous layout exception aborted the pass | Fix the FIRST exception in the console; this one disappears with it |
| `Incorrect use of ParentDataWidget` | `Expanded`/`Flexible` outside a `Flex`, or `Positioned` outside a `Stack` | Make it a DIRECT child of the right parent โ€” a `Padding` in between breaks it |
| `Cannot hit test a render box with no size` | The box was laid out with zero constraints, usually inside a zero-size parent | Give the parent a real size; check for an empty `Container` wrapper |
| `Failed assertion: 'constraints.hasBoundedHeight'` under `IntrinsicHeight` | Intrinsic sizing under unbounded constraints | Remove the intrinsic widget; intrinsics can cost O(Nยฒ) over the subtree |
| `No Material widget found` / `No Directionality widget found` | The widget is outside the app's `MaterialApp` scope | Wrap in `Material`/`MaterialApp` โ€” in tests this is the usual cause (`testing.md`) |
| `Scaffold.of() called with a context that does not contain a Scaffold` | The context is the one that CREATED the Scaffold, not one below it | A `Builder`, a child widget, or `ScaffoldMessenger.of(context)` for snackbars |
| Anything else | The console block above the stack names the offending widget and the constraints it received | Read `The relevant error-causing widget was:` and open that line |

## Rebuild Scope Ladder

Ordered cheapest to costliest. Take the first rung that removes the rebuild, and confirm it in DevTools' rebuild counter rather than by feel (`performance.md`).

1. **`const` the parts that never change.** Free at runtime, and it stops the rebuild at that boundary (rule 5).
2. **Hoist the unchanging subtree into the `child:` slot.** `AnimatedBuilder`, `ValueListenableBuilder`, and `Consumer` all take a `child` that is built once and handed back on every rebuild โ€” the highest-yield single edit in animation and list code.
3. **Extract the changing part into its own widget.** `setState` rebuilds the whole `State`'s subtree; a smaller widget is a smaller subtree. This is the fix for "setState rebuilds my entire screen".
4. **Listen to one value, not one object.** `ValueListenableBuilder`, `Selector`, `context.select`, `ref.watch(provider.select(...))` โ€” rebuild on the field you render, not on every notification.
5. **Move state DOWN, not up.** State lifted higher than its only consumer forces every sibling to rebuild. Lift only to the lowest common ancestor of the widgets that actually read it.
6. **Isolate repaints with `RepaintBoundary`.** Only after build cost is gone: it addresses painting, not building, and each boundary is a separate GPU layer with its own memory cost.
7. **Restructure with slivers or a different scroll widget.** When per-item work is genuinely unavoidable, stop rebuilding the items that are off-screen (`layout.md`).

## Widget Choice

Pick the least capable widget that expresses the need โ€” each row costs something the row above does not.

| Need | Use | Cost or trap |
|---|---|---|
| Fixed size, or space between widgets | `SizedBox` | `const`-friendly; a `Container` with only a size drags in the whole decoration pipeline |
| Padding | `Padding` | `Container(padding:)` is the same thing wrapped in more objects |
| Any list longer than one screen | `ListView.builder` / `.separated` | `ListView(children: [...])` builds and retains every item, on-screen or not |
| List with uniform row height | `ListView.builder` + `itemExtent` | Lets the viewport skip measuring items; required for instant jumps in long lists |
| Headers, grids, and lists in one scroll view | `CustomScrollView` + slivers | Nesting scrollables inside a `Column` is the wrong shape (`layout.md`) |
| Rebuild on one value | `ValueListenableBuilder` | No package needed; `setState` rebuilds the whole `State` |
| A future rendered once | `FutureBuilder` with the future held in a FIELD | Creating the future inside `build` re-runs it on every rebuild (`async.md`) |
| A continuously changing value | `StreamBuilder` over a cached stream | Re-listening to a single-subscription stream throws `Bad state: Stream has already been listened to` |
| Show/hide without losing state | `Offstage`, or `Visibility(maintainState: true)` | A conditional `if` in the child list DISPOSES the subtree โ€” often the actual bug |
| Fade, size, or color change on a state change | `AnimatedContainer`, `AnimatedOpacity`, `AnimatedSwitcher` | Implicit animations need a CHANGED value and, for `AnimatedSwitcher`, differing keys (`animations.md`) |
| One animation driving several widgets | `AnimationController` + explicit transitions | Owns a ticker: `TickerProviderStateMixin` plus a `dispose` line (rule 3) |
| Read theme, media, or an inherited value | `Theme.of`, `MediaQuery.sizeOf`, `context.watch` | Creates a dependency: the widget rebuilds whenever that value changes |
| Reach into a child's state | A callback or a shared notifier passed down | `GlobalKey` works, but forces a global registry and blocks subtree reuse (`widgets.md`) |
| Anything else | Compose from existing widgets before writing a `RenderObject` | A custom `RenderObject` is right for genuinely custom layout and a maintenance tax everywhere else |

## Output Gates

Before emitting Flutter code, verify:

- Every `await` inside a `State` is followed by a `mounted` check before `setState`, `context`, or a controller is touched
- Every controller, `FocusNode`, subscription, and timer created has a matching line in `dispose`
- Every list of stateful children carries a stable `ValueKey`, and no `UniqueKey` appears inside a builder
- No future creation, network call, navigation, snackbar, or notifier write happens inside `build`
- Static subtrees are `const`; `AnimatedBuilder`/`ValueListenableBuilder` pass their unchanging subtree through `child:`
- Long lists use `.builder`; images from network or disk set `cacheWidth`/`cacheHeight`
- Text that can grow (labels, buttons, chips) survives a large text scale without overflowing (`accessibility.md`)
- Interactive targets meet the platform minimum, and icon-only controls carry a semantic label
- `flutter analyze` is clean โ€” not merely "it compiles"

## Configuration

User-dependent variables. Defaults apply until the user states a preference; store them in `~/Clawic/data/flutter/config.yaml`. Never interview the user โ€” record a preference the moment it is stated.

| Variable | Type | Default | Effect |
|---|---|---|---|
| state_management | auto \| setstate \| provider \| riverpod \| bloc | auto | `auto` reads `pubspec.yaml` and matches the repo; the resolved value selects the idioms in `architecture.md` and the listener form used in Core Rule 8 examples |
| router | auto \| navigator \| go_router | auto | `auto` reads `pubspec.yaml`; drives every routing example, deep-link setup, and back-button pattern in `navigation.md` |
| target_platforms | list (android, ios, web, macos, windows, linux) | android, ios | Which platform sections surface in `platform.md`, `adaptive.md`, and `release.md`, and which store steps appear in the release checklist |
| target_fps | 60 \| 120 | 60 | Sets the frame budget used everywhere (1000 / target_fps ms, Core Rule 6) and therefore the jank threshold in `performance.md` and `debug.md` |
| project_layout | feature-first \| layer-first | feature-first | Where new files go, and how `architecture.md` names folders and boundaries |
| codegen | allowed \| avoid | allowed | `avoid` bans `build_runner` solutions (freezed, json_serializable, route generators) and emits hand-written equivalents in `data.md` and `architecture.md` |
| destructive_confirm | bool | true | Confirms before `flutter clean`, `--update-goldens`, and deleting `Podfile.lock`/`pubspec.lock` (`commands.md`) |

Preference areas โ€” customizable dimensions; a stated preference gets recorded in config.yaml and applied:

- **Tooling**: DI (`get_it` vs scoped providers), HTTP client (`http` vs `dio`), local storage (`drift`, `isar`, `sqflite`, `hive`), image caching package, lint set โ€” affects every "add a package" recommendation in `data.md` and `dependencies.md`
- **Conventions**: widget file granularity, naming of state and notifier classes, private-widget vs builder-method style, `const`-everywhere policy โ€” affects the shape of emitted code in `widgets.md`
- **Platform**: min SDK and deployment targets, flavor names, web hosting shape, desktop window behavior โ€” affects `release.md` and `adaptive.md`
- **Design system**: Material vs Cupertino vs custom, theming through `ThemeExtension` vs constants, dark-mode obligation โ€” affects `adaptive.md` and every styling example
- **Testing posture**: golden-test policy, coverage gate, whether integration tests run in CI, mocks vs fakes โ€” affects the gates in `testing.md`
- **Safety posture**: how proactively to raise release-only and accessibility risks vs answering only what was asked โ€” affects Output Gates verbosity
- **Integrations**: backend (Firebase, Supabase, REST, GraphQL), crash reporting, analytics, CI provider and distribution channel โ€” affects `data.md` and `release.md`

## Traps

| Trap | Why it fails | Do instead |
|---|---|---|
| Creating a controller or a future inside `build` | `build` can run many times per second; each run makes a new object, restarts the request, and abandons the old one | Create in `initState` or as a field; dispose in `dispose` (rules 2-3) |
| `Container` everywhere | It composes decoration, constraints, padding, and transform whether or not you use them, and blocks `const` | `SizedBox`, `Padding`, `ColoredBox`, `DecoratedBox` โ€” one job each |
| `UniqueKey()` to "force a refresh" | New identity every build: state, scroll offset, and animations reset every frame | `ValueKey(stableId)`, or change the value the widget renders |
| `GlobalKey` to reach another widget's state | Global lookup, blocks subtree reuse, and throws "multiple widgets used the same GlobalKey" on reparenting | Pass a callback down, or share a notifier (`architecture.md`) |
| `shrinkWrap: true` to silence an unbounded-height error | Lays out every child on every scroll frame; the list gets slower as it grows | `Expanded`, a bounded height, or slivers (`layout.md`) |
| `MediaQuery.of(context)` just for a size | Subscribes to ALL of `MediaQueryData`: the widget rebuilds on keyboard open, rotation, and inset changes | `MediaQuery.sizeOf(context)` (`flutter >=3.10`), or `LayoutBuilder` for the parent's real constraints |
| `Platform.isIOS` in shared code | `dart:io` does not exist on web โ€” the app fails to compile or throws at startup | `defaultTargetPlatform`, guarded by `kIsWeb` (`adaptive.md`) |
| `Opacity` inside an animation | Wrapping a subtree triggers `saveLayer`: an offscreen buffer allocated and composited every frame | `FadeTransition`/`AnimatedOpacity`, or animate the alpha of a leaf's color |
| Network images without `cacheWidth`/`cacheHeight` | Decoded cost is width ร— height ร— 4 bytes regardless of the compressed file size; one photo can evict the whole image cache | Decode at display size (`performance.md`) |
| `flutter clean` as the first debugging move | Deletes build outputs and guarantees the slowest possible next build; it only fixes stale-artifact bugs | Read the first exception; reach for `clean` after a plugin, SDK, or native-config change (`commands.md`) |
| Catching channel failures with a bare `catch (e)` | Hides `MissingPluginException` (registration) behind `PlatformException` (the native side said no) โ€” two different bugs | Catch `PlatformException` explicitly and branch on `code` (`platform.md`) |
| Judging performance from a debug build or a simulator | Debug is JIT plus instrumentation; simulators do not model the device GPU | `--profile` on a physical device (rule 7) |
| `if (visible) child` to hide a subtree | Removing the widget disposes its `State` โ€” the "why did my half-filled form clear itself" bug | `Offstage`, or `Visibility(maintainState: true)` when the state must survive |

## Where Experts Disagree

- **State management library.** Bloc's camp buys testability and an explicit event log and pays in ceremony per feature; the Riverpod/Provider camp buys terseness and pays with looser conventions. The line both accept: plain `setState` is correct for state owned by one widget, and state read by two sibling subtrees needs something above them. Repo consistency outranks the ranking โ€” a codebase running two of them is worse than either alone.
- **Codegen (`freezed`, `json_serializable`, route generators).** One camp treats generated code as the only defensible way to keep 200 models correct; the other refuses the `build_runner` step, the diff noise, and the CI minutes. Boundary: hand-written parsing scales to a few dozen models and stops scaling once nested optional fields appear. Set `codegen` once and stop re-litigating it.
- **`BuildContext`-free navigation.** A global `navigatorKey` makes navigation callable from anywhere (interceptors, notification handlers) and hides the tree dependency that makes routes testable. Reserve it for the few entry points with genuinely no context; anything triggered by a widget navigates with its own context.
- **Golden tests.** Advocates catch visual regressions no widget test sees; skeptics point at a suite that breaks on every font, platform, and SDK bump. Where the evidence lands: goldens pay off for design-system components on a pinned CI platform, and cost more than they return on full screens (`testing.md`).

## Related Skills
More Clawic skills, get them at https://clawic.com/skills/flutter (install if the user confirms):
- `react-native` โ€” the other cross-platform stack; when comparing or migrating
- `animate` โ€” cross-framework motion systems and reduced-motion policy
- `in-app-purchases` โ€” subscriptions and paywalls on top of a Flutter app
- `testflight` โ€” distributing the iOS build produced in `release.md`
- `app-store` โ€” store listing, metadata, and review process

## Feedback

- If useful, star it: https://clawic.com/skills/flutter
- Latest version: https://clawic.com/skills/flutter

Part of [Clawic](https://clawic.com), the verified skill library. Get this skill: https://clawic.com/skills/flutter.

Files in this skill

  • SKILL.md26.3 KB
  • _meta.json390 B
  • accessibility.md6 KB
  • adaptive.md6.5 KB
  • animations.md6.3 KB
  • architecture.md7.7 KB
  • async.md7.2 KB
  • commands.md5.2 KB
  • custom-painting.md6.7 KB
  • dart.md6.7 KB
  • data.md7.5 KB
  • debug.md9.1 KB
  • dependencies.md6.6 KB
  • forms.md7 KB
  • layout.md7.7 KB
  • localization.md6.9 KB
  • navigation.md7.1 KB
  • performance.md7.7 KB
  • platform.md6.9 KB
  • release.md6.9 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading commentsโ€ฆ