flutter · v2026.09.10 · 2026-09-10 · sha256 1dc43528502e132e
flutter v2026.09.10A
Immutable. This exact content never changes and is served at /api/v1/blob/1dc43528502e132e.
---
name: flutter
description: "Guides Flutter and Dart work end to end: widgets and responsive layout, state management (Riverpod 3 Notifier, Bloc/Cubit, ChangeNotifier MVVM), go_router navigation and platform deep links, unit/widget/integration tests with mocktail, mockito or patrol, Dart 3 language features (patterns, records, sealed classes, primary constructors), rendering and jank performance, localization with ARB files, JSON and http, and platform channels, Pigeon and FFI. Use when creating, reviewing, debugging or migrating Flutter apps and Dart packages, when reading pubspec.yaml to pick a state or routing approach, or when fixing RenderFlex overflow, unbounded-constraint and analyzer errors. Do not use for native iOS, Android, SwiftUI or Jetpack Compose code, React Native, or Flutter web hosting and store release operations."
license: MIT (upstream attributions in NOTICE.md)
metadata:
author: HyperSkills
version: "2026.09.10"
category: platform
---
# flutter
## Scope
Covers Flutter application and Dart package work: widget composition and layout,
the three state-management stacks in real use, navigation and deep links,
testing at every layer, performance, localization, networking and
serialization, Dart 3 language features, analyzer/tooling hygiene, and the
Dart↔native boundary (platform channels, Pigeon, FFI).
Not covered: writing native iOS/Android UI or platform SDK code beyond the
channel boundary — use the `apple` or `android` skill. Other cross-platform
stacks: React Native and Expo belong to the `react-native` skill. Also out of
scope: Flutter web hosting/CI/store release, Firebase and other vendor SDKs,
and game engines (Flame). For visual/UX design decisions or web frontends, use
the `frontend-design` skill instead.
Paths below are relative to this skill's directory.
## Core rules
1. Detect the stack before touching state: read `pubspec.yaml`. `flutter_riverpod` /
`hooks_riverpod` / `riverpod_annotation` → Riverpod; `flutter_bloc` → Bloc/Cubit;
neither → `ChangeNotifier` + `ListenableBuilder`. Two state solutions in one app
means two sources of truth for the same screen.
2. In Riverpod, `StateProvider`, `StateNotifierProvider` and `ChangeNotifierProvider`
are legacy — they survive only for migration. New code uses `Notifier`/`AsyncNotifier`
behind `NotifierProvider`/`AsyncNotifierProvider`.
3. Put `ProviderScope` (or the root `BlocProvider`) in `runApp`, not inside `MyApp`:
inside the app widget, provider state dies on every root rebuild and tests observe a
different tree than production.
4. Mark every constructor `const` that the analyzer allows, and prefer `Padding`,
`SizedBox`, `ColoredBox` over `Container` — a const subtree is skipped during rebuild;
`Container` is rarely const-able.
5. Extract widget classes, never `Widget _buildHeader()` helpers: a method rebuilds with
its parent, cannot be const, and shows up as an anonymous node in DevTools.
6. Any list that can exceed one screen uses `ListView.builder`/`GridView.builder`/
`SliverList`. A `Column` (or `ListView(children: [...])`) builds and lays out every
child up front, even the ones nobody scrolls to.
7. Guard `BuildContext` after every `await`: `if (!context.mounted) return;` before
`Navigator`, `ScaffoldMessenger`, `Theme.of` and friends. The widget may be gone.
8. No IO, no network calls, no `setState` inside `build()`. `build` runs on every
dependency change and every frame that invalidates the subtree.
9. Keep decisions out of widgets: formatting and show/hide are fine, anything that
transforms or decides about data belongs in the notifier/cubit/view model, which never
sees a `BuildContext`.
10. Model state as an immutable value compared by value (`Equatable`, `freezed`, or a
sealed hierarchy), and assign new collections (`state = [...state, item]`). In-place
mutation leaves `==` true and listeners silent.
11. Dispose what you create: controllers and `FocusNode`s in `dispose()`, provider
subscriptions and timers in `ref.onDispose`, streams in `Bloc.close`.
12. Base layout decisions on constraints (`LayoutBuilder`) or window size
(`MediaQuery.sizeOf`), never on device type or `orientationOf` — apps run in split
screen, on foldables and in resizable desktop windows.
13. Finish every change with the gate: `dart format .`, then `flutter analyze` with zero
issues, then `flutter test`. Analyzer info-level lints count; silence one only with
`// ignore: <code> — <reason>`.
## Workflows
### add-feature
- [ ] Read `pubspec.yaml` for the state, routing, DI and mocking packages already in use
(rule 1); follow them instead of introducing a new one.
- [ ] Place files feature-first: `lib/features/<feature>/{data,domain?,ui}/`. Never create
top-level `blocs/`, `widgets/`, `models/` buckets.
- [ ] Define the immutable state type first (sealed hierarchy, or `AsyncValue<T>` when the
state is exactly loading/data/error).
- [ ] Write the service (raw API/plugin wrapper), then the repository (single source of
truth, caching, domain models), then the notifier/cubit/view model.
- [ ] Add a use case only when logic spans repositories, is genuinely complex, or has more
than one caller.
- [ ] Build the view last: `ConsumerWidget`/`BlocBuilder`/`ListenableBuilder` renders the
state exhaustively; callbacks dispatch intents.
- [ ] Register the route in the router; register dependencies where the app already does.
- [ ] Write tests for the notifier/repository logic (see `add-tests`).
- [ ] **Gate:** `flutter analyze` reports no issues and `flutter test` passes.
### fix-layout-overflow
- [ ] Read the first exception in the console, not the cascade. `RenderBox was not laid
out` is a downstream symptom — scroll up to the real constraint violation.
- [ ] Match the message to the fix:
- `RenderFlex overflowed` → wrap the offending child in `Expanded` (must fit) or
`Flexible` (may shrink); for text also consider `softWrap`/`overflow`.
- `Vertical viewport was given unbounded height` → the scrollable sits in a `Column`;
wrap it in `Expanded`, or give it a height, or switch the parent to `CustomScrollView`.
- `An InputDecorator ... cannot have an unbounded width` → wrap the field in `Expanded`
inside the `Row`.
- `Incorrect use of ParentDataWidget` → `Expanded`/`Flexible` must be a direct child of
`Row`/`Column`/`Flex`, `Positioned` of `Stack`.
- [ ] If the constraint chain is unclear, run the app and inspect it in DevTools, or set
`debugPaintSizeEnabled = true` temporarily.
- [ ] Hot reload and resize the window through the small and large breakpoints.
- [ ] **Gate:** no exception in the console and no overflow stripes at either breakpoint.
### add-tests
- [ ] Pick the layer: pure logic and repositories → `test` (unit); one widget or screen →
`flutter_test` widget test; a full user journey on a device → `integration_test`.
- [ ] Use the project's mocking library — `mocktail` unless `pubspec.yaml` already has
`mockito` + `build_runner`. Mock at the repository or client boundary, not the
class under test.
- [ ] With `mocktail`, call `registerFallbackValue` in `setUpAll` for every custom type
used with `any()`; without it the matcher throws at runtime.
- [ ] Assert on observable behaviour: emitted states, thrown types
(`throwsA(isA<MyException>())`), rendered widgets. A test that only replays a stub
cannot fail when the code breaks.
- [ ] For Riverpod, override the dependency provider in a fresh `ProviderContainer` per
test and `addTearDown(container.dispose)`; never mock a `Notifier` with
`implements`.
- [ ] **Gate:** `flutter test` (or `dart test` for a pure Dart package) passes, and the
new test fails when the behaviour under test is broken.
### upgrade-dependencies
- [ ] Run `flutter pub outdated` and read the "Resolvable" column before editing anything.
- [ ] Upgrade within constraints first (`flutter pub upgrade`), then majors one package
per commit so a regression has one suspect.
- [ ] On a version-solving conflict, run `flutter pub deps` to find who pins the old
version; prefer bumping that package over a `dependency_overrides` entry, and delete
any override you add as soon as upstream catches up.
- [ ] Run `dart fix --dry-run`, review, then `dart fix --apply` for mechanical deprecations.
- [ ] **Gate:** `flutter analyze` clean and `flutter test` green on the upgraded lockfile.
### review
- [ ] Establish the diff scope, then read the tests in it first: they state what the author
believes the code does.
- [ ] Walk the Core rules in order — they are ordered by how often each one is the actual
defect.
- [ ] Check layer discipline: no `BuildContext` in logic classes, no service calls from a
widget, no feature reaching into another feature's internals.
- [ ] Check lifecycle: disposal, `context.mounted`, provider scope and `autoDispose`.
- [ ] Check the rebuild surface: missing `const`, whole-object `watch` where a `select`
belongs, an eager list where a lazy builder belongs.
- [ ] Report using the Output format below; run `flutter analyze` yourself rather than
guessing which findings the analyzer already covers, and skip those it does.
- [ ] **Gate:** every finding carries `path:line`, a one-line reason, and a concrete fix.
## Topic router
| Topic | Read when | File |
|---|---|---|
| Layers, folders, DI | Structuring a project or feature, or deciding where logic lives | `references/architecture.md` |
| Riverpod | The project depends on any `riverpod` package | `references/state-riverpod.md` |
| Bloc / Cubit | The project depends on `flutter_bloc` | `references/state-bloc.md` |
| Provider / ChangeNotifier | No Riverpod or Bloc; official MVVM baseline | `references/state-provider-changenotifier.md` |
| Routing, deep links | Adding routes, tabs, auth redirects, or app links | `references/navigation.md` |
| Tests | Writing or reviewing unit, widget, integration or E2E tests | `references/testing.md` |
| Widgets and layout | Building UI, adaptive layouts, or debugging constraints | `references/widgets-layout.md` |
| Performance | Jank, dropped frames, slow lists, memory growth | `references/performance.md` |
| Dart 3 language | Patterns, records, sealed classes, primary constructors | `references/dart-language.md` |
| Tooling | Analyzer config, `dart fix`, coverage, pub conflicts | `references/dart-tooling.md` |
| Localization | Adding `.arb` strings, plurals, locale handling | `references/localization.md` |
| JSON and HTTP | Models, parsing, network calls, isolates | `references/json-http.md` |
| Native interop | MethodChannel, EventChannel, Pigeon, FFI/ffigen | `references/platform-channels-ffi.md` |
| Accessibility | Semantics, screen readers, text scaling, contrast | `references/accessibility.md` |
## Output format
For `review` (and any other report on existing code), group findings by file, ordered by
severity, with no preamble:
```
lib/features/cart/ui/cart_page.dart
L42 blocking - `ref.read(cartProvider)` in build() reads once and skips rebuilds.
Use `ref.watch`.
before: final cart = ref.read(cartProvider);
after: final cart = ref.watch(cartProvider);
L88 important - ScaffoldMessenger.of(context) used after `await` without
`if (!context.mounted) return;`.
L15 minor - `EdgeInsets.all(8)` can be const.
lib/features/cart/data/cart_repository.dart
✓ pass
```
Severities: `blocking` (crash, data loss, wrong behaviour, legacy API that will be
removed), `important` (rebuild storms, leaks, layer violations), `minor` (const,
naming, dead code). End with a one-line verdict: ship, ship after blocking fixes, or
rework.
## Environment
Flutter SDK on `PATH` (`flutter --version` also reports the bundled Dart). Commands used
by the workflows:
```bash
flutter analyze # zero issues is the bar; add --fatal-infos in CI
flutter test # dart test for a pure Dart package
flutter pub outdated # before any dependency change
dart fix --apply # mechanical deprecation fixes
dart format .
```
Device-dependent steps (`flutter drive`, `patrol test`, `adb`, `xcrun simctl`) need a
connected device, emulator or simulator; iOS steps additionally need macOS with Xcode.