Agent skill
flutter-ui-design
Flutter UI実装のアーキテクチャ規約・コンポーネント分割・状態管理ガイド(Bloc/Cubit版)
Install this agent skill to your Project
npx add-skill https://github.com/K9i-0/ccpocket/tree/main/.claude/skills/flutter-ui-design
SKILL.md
Flutter UI 実装規約
アーキテクチャ概要
SSOT (Single Source of Truth) + UDF (Unidirectional Data Flow) に基づく設計。
データフローパターン
- Path A (Query): Cubit/Bloc → Widget (BlocBuilder/BlocListener)
- サーバー状態、永続化データ、共有状態
- BlocProvider を通じて単方向に流れる
- Path B (Command): Widget → Cubit method → State emit
- ユーザーアクション、API呼び出し
- Cubit のメソッド経由で状態を変更
- Path C (Local): StatefulWidget / useState
- テキスト入力、スクロール位置、展開状態等の一時的UI状態
Widget 分割ルール
禁止パターン
// NG: プライベートメソッドでのWidget分割
class MyScreen extends StatefulWidget {
Widget _buildHeader() { ... }
Widget _buildBody() { ... }
Widget _buildFooter() { ... }
}
推奨パターン
// OK: 独立したWidgetクラスに分割
class MyScreenHeader extends StatelessWidget { ... }
class MyScreenBody extends StatelessWidget { ... }
class MyScreenFooter extends StatelessWidget { ... }
分割の判断基準
- 20行以上のbuildメソッド内ブロック → 独立Widgetに
- 独自のCubitを持つ → 独立Widget + BlocProvider
- BlocBuilder を含む → 独立Widget
- 表示のみ → StatelessWidget
状態管理
Cubit パターン
class ChatSessionCubit extends Cubit<ChatSessionState> {
ChatSessionCubit() : super(const ChatSessionState());
void sendMessage(String text) {
// Command (Path B)
emit(state.copyWith(/* ... */));
}
}
BridgeCubit パターン(Stream購読)
class ConnectionCubit extends BridgeCubit<BridgeConnectionState> {
ConnectionCubit(super.initialState, super.stream);
}
Freezed State
@freezed
class ChatSessionState with _$ChatSessionState {
const factory ChatSessionState({
@Default([]) List<ChatEntry> entries,
@Default(SessionStatus.idle) SessionStatus status,
}) = _ChatSessionState;
}
- 全ての状態クラスは Freezed で定義
- sealed union で排他的状態を表現
@Defaultで初期値を明示
ファイル構成
feature-first 構造
lib/features/<feature>/
├── <feature>_screen.dart # 画面Widget
├── state/
│ ├── <feature>_state.dart # Freezed state classes
│ ├── <feature>_cubit.dart # Cubit
│ └── <feature>_state.freezed.dart # 生成ファイル
└── widgets/
├── <component_a>.dart # 独立Widget
└── <component_b>.dart
命名規約
| 種別 | 命名 | 例 |
|---|---|---|
| 画面 | *_screen.dart |
chat_screen.dart |
| 状態 | *_state.dart |
chat_session_state.dart |
| Cubit | *_cubit.dart |
chat_session_cubit.dart |
| Widget | 機能を表す名前 | chat_app_bar.dart |
ValueKey 命名規約(MCP自動テスト対応)
UI要素にはValueKeyを付与し、Marionette MCPでの自動テストを可能にする。
命名パターン
{要素の機能}_{要素タイプ}
例
ElevatedButton(
key: const ValueKey('approve_button'),
onPressed: _approve,
child: const Text('Approve'),
)
TextField(
key: const ValueKey('message_input'),
controller: _controller,
)
要素タイプ一覧
| タイプ | 用途 |
|---|---|
_button |
ボタン |
_field |
テキスト入力 |
_input |
テキスト入力(短い) |
_list |
リスト |
_fab |
FloatingActionButton |
_toggle |
トグル |
_chip |
チップ |
_badge |
バッジ |
_indicator |
インジケーター |
Flutter ベストプラクティス
Flutter公式AIルール (flutter/flutter docs/rules) から、本プロジェクトに適用可能なものを抜粋。
パフォーマンス
- build()内で重い処理をしない: ネットワーク呼び出し・複雑な計算はbuild()の外で行う
- ListView.builder / SliverList: 長いリストは必ずbuilder系コンストラクタで遅延生成する
- constコンストラクタ: Widget・build()内で可能な限り
constを使いリビルドを削減する - Isolate: JSON解析等の重い処理は
compute()で別Isolateに逃がす
Dartコーディング
- Null Safety: ! (bang operator) は値がnon-nullと保証できる場合のみ使用。安易に使わない
- exhaustive switch: switch文/式は網羅的に書く。breakは不要
- パターンマッチング: コードを簡潔にできる箇所ではパターンマッチングを活用する
- アロー関数: 1行で済む関数はアロー構文 (
=>) を使う - 関数の長さ: 1関数20行未満を目指す。超える場合は分割を検討
レイアウト
- Expanded / Flexible: 同一Row/Column内での混在禁止
- Wrap: Row/Columnで溢れる要素はWrapで折り返す
- SingleChildScrollView: 固定サイズでビューポートを超えるコンテンツに使用
- FittedBox: 子Widgetを親のサイズに合わせてスケーリング
- LayoutBuilder: レスポンシブレイアウトでの利用可能スペースに基づく分岐
テーマ・スタイリング
- ThemeExtension: 標準ThemeDataに無いカスタムスタイルはThemeExtensionで定義する
- ColorScheme.fromSeed(): シードカラーからLight/Dark両テーマを生成
- WidgetStateProperty: ボタン等の状態別スタイルは
resolveWithで定義
アクセシビリティ
- コントラスト比: テキストは背景に対して4.5:1以上(大きいテキストは3:1以上)
- Semantics: スクリーンリーダー向けに
SemanticsWidgetで説明ラベルを付与 - 動的テキストスケーリング: システムフォントサイズ変更時にUIが崩れないことを確認
build_runner
状態クラスの変更後は必ず実行:
cd apps/mobile && dart run build_runner build --delete-conflicting-outputs
チェックリスト
実装完了時に確認:
-
_buildXxx()メソッドが残っていないこと - 全状態がFreezedクラスで管理されていること
- BlocBuilder/BlocListenerが適切に使い分けられていること
- 新規UI要素にValueKeyが付与されていること
-
dart analyze apps/mobileがクリーン -
dart format apps/mobileが適用済み - 既存テストがパス (
flutter test) - 新規Cubitのユニットテストが追加されていること
- build()内に重い処理(ネットワーク、複雑な計算)がないこと
- 長いリストがListView.builder/SliverListで実装されていること
- 可能な箇所でconstコンストラクタが使われていること
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
triage
GitHub Issue・PRのトリアージ。番号を渡すと、要望の要約・実現難易度・既存機能との重複チェック・対応判断を調査してレポートする。Issue/PRの番号が出てきたとき、トリアージ、優先度判断、対応判断と言われたときに使用する。
self-review
タスク完了前のセルフレビュー。Claude subagentで別コンテキストから客観的にコード変更を検証。
release-bridge
Bridge Server のリリース(バージョンbump + CHANGELOG + タグ → GH Actions で npm publish)
flutter-upgrade
Flutter SDKバージョンアップグレード対応。新バージョンのリリースノート・Breaking Changes調査、コードベース影響分析、mise/CI/Shorebird含むプロジェクト全体の対応タスクリスト作成と実行。「Flutterアップグレード」「Flutter X.Y.Zがリリースされた」「Flutter最新化」「Flutter更新」と言われたとき、またはFlutterの新バージョンについて言及されたときに使用する。
test-bridge
Bridge Server (TypeScript) のテスト実行・型チェック・テスト記述ガイド
shorebird-patch
Shorebird OTA パッチの作成・staging 配布(stable 昇格はユーザー実施)
Didn't find tool you were looking for?