building-flutter-apps について
Flutter と Dart の開発を支援し、シナリオに応じた規則や参照資料をコーディングエージェントに示すスキルです。Riverpod、アーキテクチャ、テスト、アクセシビリティ、ローカライズを扱います。
主な機能
- 01シナリオに応じた参照資料の選択
- 02Flutter と Riverpod の実装規則
- 03アーキテクチャと状態管理の指針
- 04テスト、アクセシビリティ、ローカライズの規則
- 05ファイル変更後の事前チェック
活用シーン
- 01Flutter アプリの新規作成や変更
- 02Riverpod の状態や非同期処理の実装
- 03UI、アクセシビリティ、ナビゲーション変更の確認
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ sgaabdu4/ building-flutter-apps --skill building-flutter-appsインストール後、Claude Code で/building-flutter-appsを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
agents/openai.yamlassets/defender-installer-scan.ps1inno-bundle-pubspec.yamlinno-uninstall-settlement-sentinel.ps1windows-installer-workflow.ymlreferences/analysis_options.yamlanalysis-options.mdarchitecture.mdatomic-design.mdbuild-reproducibility.mdcommon-patterns.mdcore-stack.mddart-decimate.mddart-mcp-e2e-testing.mddart-patterns-records.mddeep-linking.mderror-reporting.mdflutter-optimizations.mdfreezed-sealed.mdhive-persistence.mdreferences/atomic-design/accessibility.mdreferences/common-patterns/debounce-gate-batch.mddelta-sync.mdlists-forms-workflows.mdmodals-navigation.mdnavigation-flow.mdrouting-app-shell.mdreferences/extensions/collections-helpers.mdcontext-ui.mdprimitive-formatting.md詳細
最初にお読みください
- このスキルは一般的な Flutter/Dart のアドバイスより優先されます。Critical Rules は、例、公開ドキュメント、古いプロジェクトコードより優先されます。
- 各リファレンスの
Read firstセクションを正とします。 .dart/pubspec.yaml/build.yaml/analysis_options.yamlの書き込みを行うたびに、Pre-Flight を実行してください。適用されるチェックは、そこで引用されているルール/リファレンスに従います。
段階的開示のゲート
コードを書く前に、該当する Trigger Map の行だけを読んでください。シナリオ/サブシステムの行は、スタックやファイル名に付随する語句も対象とします。該当するシナリオの行がない場合に限り、common-patterns.md などの親リファレンスを読んでください。references/ をまとめて読んではいけません。Pre-Flight では、参照したリファレンスを正確に記載してください。
Critical Rules
| ID | ルール | 詳細参照 |
|---|---|---|
| R1 | Flutter/Riverpod パッケージでは、まず flutter_skill_lints と riverpod_lint を設定し、その後パッケージのルートで dart analyze とプロジェクト独自の Dart Decimate チェックを実行します。純粋な Dart CLI では、どちらのプラグインも使わず、Dart ネイティブの分析プロファイルを使用します。 | analysis-options.md, dart-decimate.md, setup.md |
| R2 | すべてのプロバイダーには @riverpod / @Riverpod コード生成を使用します。手動のプロバイダークラスやレガシーなプロバイダーファミリーは使用しません。 | riverpod-codegen.md |
| R3 | 非同期処理によるギャップは ref.mounted / context.mounted でガードします。finally では if (ref.mounted) { ... } を使用します。 | async-mutations.md |
| R4 | ウィジェットはパブリッククラスにします。_buildXxx()、ウィジェットのトップレベルヘルパー、State を除くプライベートなウィジェットクラスは使用しません。 | atomic-design.md, performance.md |
| R5 | Null 許容性は意味に基づいて決めます。空値/null/bool を番兵値として使うフォールバック、value!、Null 許容コレクション、必須のドメイン文字列の生値は使用しません。 | value-objects.md, freezed-sealed.md; Lint: domain_raw_required_string |
| R6 | ユーザー向け文字列、ツールチップ、セマンティクス、表示されるアクセシビリティ文言にはすべて AppLocalizations を使用します。 | localization.md, accessibility.md |
| R7 | 不変の状態/エンティティには sealed Freezed を使用し、1ファイルにつき宣言は1つとします。ネイティブの switch を使用し、VO の map/when は無効にします。 | freezed-sealed.md, value-objects.md |
| R8 | presentation/widgets/ は不変の入力を描画し、型付きコールバックを出力します。画面/ルート/ノーティファイアーが、ナビゲーション、ワークフロー、ドメイン状態、インフラストラクチャを担います。 | presentation-widgets.md |
| R9 | 2つ以上のクラスで重複する動作は、on 節を持つ小さなステートレスな *Mixin にします。 | mixins.md |
| R10 | ストレージ SDK の呼び出しはリポジトリの背後にあるローカルデータソースに配置します。本番用の Hive インポートには hive_ce_flutter を使用します。 | hive-persistence.md, architecture.md |
| R11 | プリミティブ/コンテキスト/コレクションの操作は core/extensions/ に配置します。ドメインからこれらの拡張機能をインポートしません。 | context-ui.md, primitive-formatting.md, collections-helpers.md |
| R12 | 意味を持つドメインプリミティブは、検証済みの Freezed Value Object にします。Hive モデルはプリミティブを保持し、マッパーで橋渡しします。 | value-objects.md, hive-persistence.md; Lint: domain_unit_primitive |
| R13 | 型付き GoRouter ルートをナビゲーションの SSOT とします。リダイレクトは純粋なリゾルバーロジックとし、ID による取得ができない場合のフォールバック UI は null 許容にします。 | deep-linking.md, routing-app-shell.md |
| R14 | ダイアログ/シートは不変のスナップショットを描画して結果を pop し、変更や破棄処理はノーティファイアーに任せます。 | modals-navigation.md, state-management-lifecycle.md |
| R15 | 高頻度の UI、同期、永続化、リモート関数、リセット、検索の境界では、デバウンス、ゲート、バッチ処理を行います。 | debounce-gate-batch.md |
| R16 | アプリシェルは宣言的に保ちます。ブートストラップのリスナーは、ルートと並列に配置した ConsumerWidget に置きます。 | routing-app-shell.md |
| R17 | 制御フローは処理の終了後にフラットに保ちます。return / throw / break / continue の後にある不要な else は削除します。 | Lint: avoid_unnecessary_else_after_control_flow |
| R18 | onReorderItem の削除後インデックスをそのまま使用します。レガシーな onReorder の変換計算は追加しません。 | Lint: use_on_reorder_item_index_semantics |
| R19 | Android の正確なアラームには、手動の設定インテントではなく flutter_local_notifications の権限 API を使用します。 | Lint: use_local_notifications_exact_alarm_permission_api |
| R20 | プラットフォーム固有のメンバーを呼び出す前に、null 許容のプラグイン実装を解決します。 | Lint: resolve_platform_specific_implementation_before_use |
| R21 | ウィジェットプレビューはプレビュー専用とし、決定論的なフェイクを使用します。実際の HTTP/Firebase/Hive/ネイティブプラグインは使用しません。 | widget-previews.md |
| R22 | ランタイム E2E では、安定したセレクター、失敗を検出できるシナリオ/ログ、対象に一致した証拠、情報源の検証、クリーンアップ、必要に応じた複数アクターによる証明を通じて動作を証明します。 | dart-mcp-e2e-testing.md |
| R23 | アクセシビリティは UI の正しさの一部です。ローカライズされたツールチップ/セマンティックラベル、48x48 のタップ領域、コントラスト、テキストスケール、Text.rich を使用します。 | accessibility.md, flutter-optimizations.md |
| R24 | リモートエラー報告が承認済み、またはすでに導入済みの場合は、アプリが所有する Crash 境界を1つだけ設け、操作ごとの報告主体を1つにします。それ以外の場合は、プロバイダー/ファサードを追加しません。テレメトリー送信前に機密データを除去し、結果が不確定なリモート処理の状態を確認します。 | error-reporting.md, networking.md |
| R25 | Windows インストーラーの配布では、単一のセマンティックエンジン、型付きのアプリ設定/機能、最小ステップで正確な SHA を診断して単一の配布元へ渡します。1回のビルドの前に低コストなガードを実行し、合成的な保持証明を分離し、検証済みの不変なバイト列のみを有効化します。 | windows-installer-pipeline.md, ワークフローのひな形, inno_bundle pubspec のひな形, Inno の決済センチネル, Defender スキャナー |
| R26 | 一時停止の影響を受ける Riverpod の状態は、永続的な所有者/リスナーが存在してから開始します。派生状態は基底状態を直接監視します。認証/フォームのモード切り替え時には一時的なエラーをクリアします。 | notifier-structure.md, state-management-lifecycle.md, testing.md |
| R27 | ネイティブ/カスタムリンクでは、生成元、プラットフォーム登録、Flutter への受け渡し、型付きルーターで同一の URI 契約を使用します。対象デバイスでコールド/ウォーム起動時の動作と署名済み状態の受け渡しを検証します。 | deep-linking.md, dart-mcp-e2e-testing.md |
Trigger Map
| 内容 | 参照先 |
|---|---|
新規アプリ/プロジェクトのひな形作成に伴うスタック/パッケージへの言及、main.dart、ProviderScope、MaterialApp.router、アプリ起動時のシェル | setup.md + architecture.md + routing-app-shell.md |
Notifier/AsyncNotifier の構造、同期的な Notifier.build() 初期化、一時停止中のルート/リスナーの起動、プロバイダーの投影、認証/フォームモードのエラーリセット、ローディング/進捗、AsyncValue、クリーンアップ | notifier-structure.md + state-management-lifecycle.md + testing.md |
ミューテーションメソッド、ref.read / ref.watch / ref.listen、_ensureRepository、非同期キャンセル、ref.mounted、楽観的更新、重複フェッチ | async-mutations.md + state-management-lifecycle.md |
Freezed エンティティ、シールドユニオン、fromJson / toJson、copyWith、モデルとエンティティの違い、explicit_to_json 用の build.yaml | freezed-sealed.md |
プロバイダー宣言、@riverpod、ファミリー、keepAlive、コード生成、Mutation<T>(実験的) | riverpod-codegen.md |
リポジトリ、データソース、ドメインエンティティ、レイヤードアーキテクチャ、IHttpService、モデルからエンティティへのマッピング | architecture.md |
値オブジェクト、プリミティブ型への過度な依存、Distance/Money/Email/Slug、ドメイン層での単位変換、複数エンティティにまたがるプリミティブ型、double distanceMeters/int amountCents/String email の問題、arch_domain_import エラー | value-objects.md |
GoRouter、型付きルート、リダイレクト、認証で保護されたルート、ルータープロバイダー、context.go、ディープリンク、カスタム URI スキーム、ネイティブ拡張機能/アクティビティリンク、コールドスタート、ナビゲーションゲート | routing-app-shell.md + deep-linking.md |
| HTTP、ネットワーク、REST、ミューテーション後の信頼できる唯一の情報源からのフェッチ、長時間実行されるリモート関数、非同期開始と整合処理、トランスポート ID とドメイン ID | networking.md + debounce-gate-batch.md |
アトム、モレキュール、オーガニズム、デザイントークン、アトミックウィジェット、core/widgets/ への昇格 | atomic-design.md |
再利用可能な presentation/widgets/、ウィジェットが所有するナビゲーション/ページスタック/選択中のエンティティ/ワークフロー状態、リポジトリ/サービス/プロバイダーへの直接アクセス | presentation-widgets.md |
| アクセシビリティ、セマンティクス、ツールチップ、semanticLabel、画像の代替テキスト、タップ領域、コントラスト、テキスト拡大 | accessibility.md + flutter-optimizations.md |
ウィジェットテスト、ProviderContainer.test()、UncontrolledProviderScope、フェイク、モック、AppWidgetKeys、イベント契約テスト | testing.md |
flutter_driver、Dart MCP、Marionette MCP、E2E、integration_test、セマンティックセレクター、シナリオ検証、スクリーンショット/メディアによる証拠、ログ取得、ネイティブ統合ビルドは成功するがデバイス上で失敗するケース、実行時権限、プラグインのハング、リリース時のみ発生する実行時エラー | dart-mcp-e2e-testing.md |
Hive、TypeAdapter、TypeId、ボックス、永続化のマイグレーション、廃止したフィールドの記録 | hive-persistence.md |
Crashlytics、FirebaseCrashlytics、Sentry、sentry_flutter、DSN、エラー報告、Crash.init、Crash.error、Crash.log、シンボルのアップロード | error-reporting.md |
| Mixin、インターフェースとケイパビリティの違い、再試行ヘルパー、RNG、一括操作 | mixins.md |
サービス、シングルトン、ファイア・アンド・フォーゲット、abstract final class、unawaited()、Future<void> シグネチャ | services-and-singletons.md |
@Preview、widget_previews.dart、プレビュー用フェイク、決定論的なプレビューデータ | widget-previews.md |
AppLocalizations、ARB ファイル、gen-l10n、ロケールのフォールバック、プレースホルダー、複数形/選択 | localization.md |
パフォーマンス、ビルドコスト、.select()、const コンストラクター、ListView.builder、大きなリストの計算 | performance.md + flutter-optimizations.md |
LayoutBuilder、RenderFlex のオーバーフロー、Row / Column の外での Expanded / Flexible、Stack の外での Positioned、テキスト拡大率の上限設定 | layout-diagnostics.md |
| ページネーション、無限スクロール、カーソルによる読み込み、検索のデバウンス、登録/フォームの検証と送信、バッチ処理、プル・トゥ・リフレッシュ | lists-forms-workflows.md + async-mutations.md |
BuildContext ヘルパー、現在のルートを確認する ModalRoute、ダイアログ、SnackBarUtils、Notifier からのスナックバー通知 | context-ui.md |
DateTime の書式設定/差分/timeAgo/startOfDay、String の capitalize/truncate/titleCase/initials/format、int / double / num の clamp/pluralized/asCurrency/percent/toFixed、Duration の書式設定、NumberFormat、DateFormat、intl | primitive-formatting.md |
Iterable の検索/インデックス指定、ウィジェットリストのヘルパー、Debouncer、バリデーター、Result、拡張型、core/extensions/ のバレルエクスポート | collections-helpers.md |
レコード (x, y)、拡張型 ID、パターンマッチング、プライマリ/簡潔コンストラクター、new()、factory()、.center などのドット省略記法、@RecordUse | dart-patterns-records.md |
Flutter/Riverpod の analysis_options.yaml、dart analyze、プラグインの接続、riverpod_lint のバージョン固定、アナライザーのクラッシュ | analysis-options.md + analysis_options.yaml |
| スキルのセットアップ、Git の pre-push、フック/スキャナーの登録 | setup.md |
build_runner、生成パーツの不足、クリーンチェックアウト、Xcode の選択、Flutter の SwiftPM 生成パッケージ、Apple デバイスのビルド、ローカルと CI の不一致 | build-reproducibility.md + core-stack.md |
| パッケージの制約、依存関係のアップグレード、ジェネレーターとアナライザーの互換性 | core-stack.md |
Flutter Windows デスクトップのパッケージ化、GitHub Actions による Windows インストーラー、Inno Setup、inno_bundle、アップデーター/自動更新、CRT DLL、PowerShell/ネイティブインストーラープロセス、インストーラー/バージョン/AppId の失敗 | windows-installer-pipeline.md + build-reproducibility.md + core-stack.md |
| Dart Decimate、デッドコード、循環依存、重複コード、複雑度、依存関係の健全性、検出件数ゼロを目指す全体スキャン | dart-decimate.md |
| より具体的な行では扱われていないナビゲーション/フォーム/リスト/デバウンス/ルートパラメーターのフォールバックパターン | common-patterns.md |
| リモートからの差分プル、デルタトークン、テーブルごとの同期日付、マージ/削除の整合処理 | delta-sync.md |
| ルートパラメーターの安全性、ウィザードの順序制御、次のステップへのガード付きナビゲーション | navigation-flow.md |
| ダイアログ/シート/モーダル、スナップショット値オブジェクト、await 後のティアダウン、閉じてからルート遷移、pop のフォールバック、ネストされたナビゲーターの閉じ方 | modals-navigation.md + state-management-lifecycle.md |
デバウンス/スロットル/集約 — TextField.onChanged、Slider.onChanged、スクロールリスナー、同期的な saveAll、一部のミューテーション後のコレクション全体の書き換え、永続化ヘルパー、センチネル値のリセット/クリア時の保持、_userTapped ゲート、build 内の WebView / VideoPlayer、サービス内の _storage.read、ref.listenManual の禁止、keepAlive コレクションの監視、データソースのバッチローダー、ゼロ値の保存ガード、Notifier 境界でのプリミティブ型から値オブジェクトへの変換、モーダルヘルパーの routeSettings | debounce-gate-batch.md |
事前確認
.dart / pubspec.yaml / build.yaml / analysis_options.yaml の書き込みバッチごとに内部確認します。常に T0 を実施し、状態/Notifier/ミューテーションの変更がある場合は T1 も、ネットワーク/E2E/ストリーム/ルートの変更がある場合は T2 も実施します。編集ループでは analysis-options.md に従い、編集したファイルのみを解析します。パッケージルート全体の実行は引き渡し前に 1 回だけ行います。
- 項目に失敗した場合 → ルール ID または参照先を添えて、速やかに報告します。
- 引き渡し時 → 完了した検証内容を報告します。参照した資料、実行したチェック、結果を記載します。
T0 — コア
- Flutter/Riverpod パッケージ:編集ループで編集したファイルの解析エラーがないこと。引き渡し前に 1 回、パッケージルートで
dart analyze --fatal-infosを実行し、flutter_skill_lintsとriverpod_lintを適用して終了コード 0 になること。セットアップの変更では、各プラグインから診断が 1 件出ることを確認します。純粋な Dart CLI:ネイティブ Dart の解析プロファイルを適用し、両プラグインは対象外です。 - 現行の同一スコープの、プロジェクト所有の Dart Decimate の結果が、dart-decimate.md に従って成功していること。スキャン範囲を明記します。
- 非同期の処理の中断後はガードします。
ref.mounted/context.mountedを使い、単独のmountedは使いません。また、finallyではif (ref.mounted) { ... }を使います。 - プロバイダー、状態、ウィジェットがルール 2〜8 および 14 に従っていること。再利用可能なウィジェットが所有するのは UI のライフサイクルのみです。画面/ルート/Notifier が、ナビゲーション、ワークフローの分岐、選択中のドメインレコード、プロバイダー状態、インフラストラクチャを所有します。
- ドメイン/データ/プラットフォームがルール 7、10〜13、17〜24、26〜27 に従っていること。sealed Freezed、VO、データソース/リポジトリのストレージ、コア拡張、型付きルート、デバウンス/バッチ、プラットフォーム API、プレビュー、E2E、一時停止に安全な状態、ネイティブリンク、アクセシビリティを確認します。エラー報告が採用済みまたは実装済みの場合は、秘匿化した一度限りの境界を 1 つ使います。それ以外は対象外です。
- ルール 25 は、Windows のパッケージ化/アップデーターによる配布に手を加えない限り対象外です。該当する場合は、その診断/パブリッシャーの証明が成功していること。
- Trigger Map で変更した行はすべて読みます。スキャナーでルールを適用すべき場合は、正確な lint 名を明記します。
T1 — 状態/Notifier/ミューテーション
- ミューテーションの依存関係は、ステートレスなヘルパー/mixin を介して遅延解決します。破棄可能なライフサイクル所有者を除き、Notifier 内でリポジトリ/サービスをキャッシュしません。
- 同期
Notifier.build()では、値を返す前にstateを読みません。主要な状態が非同期の場合はAsyncNotifier.buildを使います。永続的な同期起動を、所有者/リスナーが存在する前のマイクロタスクのタイミングに依存させません。 -
ref.onDispose()でサブスクリプション/コントローラー/タイマーをキャンセルします。永続的なステータス/スナックバー/ティアダウンは Notifier の状態に保持します。 - 長時間実行する同期/認証/インポートでは、古い書き込みを防ぎます。Notifier のメソッド内で
ref.watchは使いません。 - 一時停止の影響を受けるプロジェクションは、ベース状態を直接監視します。ルートの一時停止/再開後も最初の更新を維持します。認証/フォームのモードが変わったときは、一時的なエラーをクリアします。
T2 — ネットワーク/E2E/ストリーム/ルート
- 生成、正規化、並べ替え、破壊的変更、またはリモート関数のミューテーション後に、信頼できる情報源から取得/照合します。
- 共有/リアルタイム状態について、手動更新なしでのライター+オブザーバーの E2E 証明があります。
- セレクターは、安定したテキスト/セマンティクス/ツールチップ、または一元管理された
AppWidgetKeysを使います。インライン文字列キーや、座標による主要なタップ操作は使いません。 - E2E シナリオの状態は決定的で、本番の動作から分離されています。Marionette の実行では
lib/main.dartのデバッグ時のみ有効なバインディングを使い、Flutter Driver の実行では別の driver-extension エントリーポイントを使います。不明なシナリオは失敗させ、重大なログがあれば実行を失敗させます。アプリの終了前に、アサーション対象の画面を証拠で確認できること。クリーンアップを検証します。 - GoRouter のリダイレクトでは、純粋な関数で行列テスト済みのリゾルバー、nullable な ID 別プロバイダー/フォールバック UI、生成された型付きルートヘルパーを使います。
- ネイティブ/カスタム URI の生成元、Android/iOS の登録、Flutter への受け渡し、型付きルーターで、テスト済みの同一の scheme/host/path 契約を共有します。コールド/ウォーム起動と、署名済み状態でのデバイス経路が成功すること。
- ランタイム間で共有する定数、スキーマ、関数の契約にはドリフトテストがあります。アプリルートでテキストスケールを制限しません。
コメント
Markdown 対応