ui-package について
再利用可能な UI パッケージを作る Flutter 開発者向けガイドです。Material ウィジェット、`ThemeExtension` のデザイントークン、一貫したウィジェット API を軸に構成します。パッケージ構造、公開 API、ウィジェットテスト、Widgetbook カタログも扱います。
主な機能
- 01Material ウィジェットによる UI 構成
- 02`ThemeExtension` によるテーマトークンの拡張
- 03単一のバレルファイルで公開 API を提供
- 04各ウィジェットの振る舞いテスト
- 05`pumpApp` と Widgetbook のスキャフォールド
活用シーン
- 01再利用可能な Flutter UI パッケージの作成
- 02間隔やカスタムカラーのトークン定義
- 03Material に基づく共有コンポーネントの実装
- 04ウィジェット操作のテストとカタログ整理
MCP 対応
UI パッケージのスキャフォールドには、`subcommand` を `app_ui_package` に設定した Very Good CLI MCP の `create` ツールを使うよう定めています。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ verygoodopensource/ vgv-ai-flutter-plugin --skill ui-packageインストール後、Claude Code で/ui-packageを入力して読み込まれたか確認します。
同梱ファイルagents/openai.yamlreference.md
詳細
UI パッケージ
package:flutter/material.dart を基盤とする再利用可能なウィジェットライブラリ、Flutter UI パッケージを作成するためのベストプラクティスです。アプリ固有のコンポーネント、ThemeExtension によるカスタムデザイントークン、一貫した API サーフェスで拡張します。
テーマ設定の基盤: このスキルでは、UI パッケージの構成、ウィジェット API、テストに焦点を当てます。Material 3 の基本的なテーマ設定(
ColorScheme、TextTheme、コンポーネントテーマ、スペーシング定数、ライト/ダークモード)については、Material Theming スキル(/material-theming)を参照してください。2 つのスキルは相互に補完するものです。Material Theming ではThemeDataの設定方法と使い方を扱い、このスキルではThemeExtensionトークンを使って拡張し、その周りに再利用可能なウィジェットをパッケージ化する方法を扱います。
基本標準
UI パッケージに関するすべての作業で、次の標準を適用してください。
app_ui_packageテンプレートから作成する — Very Good CLI MCP ツールのsubcommand: 'app_ui_package'を使ってパッケージを作成します。flutter_package、dart_package、flutter create --template=packageは使用しないでください。これらでは、以下に示すテーマ、バレル、テストヘルパー、Widgetbook のスキャフォールディングがない、最小限のパッケージが生成されます- Material を基盤にする —
flutter/material.dartに依存し、Material ウィジェットを組み合わせます。Material がすでに提供しているプリミティブを作り直さないでください - 1 ファイルにつき 1 ウィジェット — 各パブリックウィジェットは、ウィジェット名に基づくスネークケースのファイル名(例:
app_button.dart)で、独立したファイルに配置します - パブリック API 用のバレルファイル — すべてのパブリックウィジェットとテーマクラスを、単一のバレルファイル(例:
lib/my_ui.dart)から公開します。このファイルではmaterial.dartも再エクスポートするため、1 つのインポートで Material のウィジェットとパッケージ独自のウィジェットの両方を利用でき、Material を別途インポートする必要がありません lib/src/はプライベート — 利用側はバレルのみをインポートし、それ以外はインポートしません。package:my_ui/src/widgets/app_button.dartのようにファイル単位でインポートすると、プライベートな実装にアクセスすることになり、内部の名前変更で壊れます。ツリーシェイキングによって未使用のウィジェットはすでに除外されるため、バレルによるコストはありませんThemeExtensionでテーマを拡張する — Material のThemeData、ColorScheme、TextThemeを基盤として使います(Material Theming スキルを参照)。アプリ固有のトークン(スペーシング、カスタムカラー)はThemeExtension<T>で追加し、それぞれをThemeData.extensionsに登録して、BuildContext拡張(context.appColors、context.appSpacing)から読み取ります- すべてのウィジェットに対応するウィジェットテストを用意する — 動作テストでインタラクション、コールバック、状態の変化を検証します。インラインの
MaterialAppではなく、パッケージのpumpAppヘルパーを使ってポンプします - アンチパターンには動作するコードで対応する — アンチパターンの表にある内容を求められた場合は、なぜ誤りなのかを説明し、同じ回答内で修正版の実装を提示します。異議を述べるだけで終わらせたり、ドキュメントに記載された方法で進めてよいか尋ねたりしないでください
- すべてのパブリッククラスにプレフィックスを付ける — Material ウィジェットとの名前の衝突を避けるため、一貫したプレフィックス(例:
App、Vg)を使います - 可能な限りすべてのコンストラクターを
constにする — 可能な場合、すべてのウィジェットコンストラクターをconstにします - すべてのパブリックメンバーをドキュメント化する — すべてのパブリッククラス、コンストラクターパラメーター、メソッドに dartdoc コメントを付けます
パッケージ構成
my_ui/
├── lib/
│ ├── my_ui.dart # Barrel file — re-exports material.dart + all public API
│ └── src/
│ ├── theme/
│ │ ├── app_theme.dart # AppTheme class with light/dark ThemeData builders
│ │ ├── app_colors.dart # AppColors ThemeExtension for custom color tokens
│ │ ├── app_spacing.dart # AppSpacing ThemeExtension for spacing tokens
│ │ └── app_text_styles.dart # Optional: extra text styles beyond Material's TextTheme
│ ├── widgets/
│ │ ├── app_button.dart
│ │ ├── app_text_field.dart
│ │ ├── app_card.dart
│ │ └── ...
│ └── extensions/
│ └── build_context_extensions.dart # context.appColors, context.appSpacing shortcuts
├── test/
│ ├── src/
│ │ ├── theme/
│ │ │ └── app_theme_test.dart
│ │ └── widgets/
│ │ ├── app_button_test.dart
│ │ └── ...
│ └── helpers/
│ └── pump_app.dart # Test helper wrapping widgets in MaterialApp + theme
├── widgetbook/ # Widgetbook catalog submodule (sandbox + showcase)
│ └── ...
└── pubspec.yaml
ウィジェットの作成
ウィジェット API のガイドライン
- Material ウィジェットを組み合わせる —
FilledButton、OutlinedButton、TextField、Cardなどを構成要素として使います - 必要最小限のパラメーターのみを受け付ける — 「何でも入り」のコンストラクターは避けます
keyとchild/children以外はすべて名前付きパラメーターにします- パラメーターが指定されていない場合は、テーマに基づく適切なデフォルト値を設定します
- コールバックには
ValueChanged<T>またはVoidCallbackを公開します — 生のFunctionは使わないでください - スロットベースの任意の構成(先行アイコン、後続アイコンなど)には
Widget?を使います
アンチパターン
| アンチパターン | 正しいアプローチ |
|---|---|
Material にすでに用意されているウィジェットを再構築する(例:GestureDetector と DecoratedBox を使ったカスタムボタン) | Material ウィジェット(FilledButton、OutlinedButton)を組み合わせてスタイルを設定する |
カスタムの InheritedWidget で独自のテーマシステムを作成する | Material の ThemeData をベースとして使い、カスタムトークンには ThemeExtension を使う |
ウィジェットのコードに Color(0xFF...) をハードコードする | 標準色には Theme.of(context).colorScheme を、カスタムトークンには context.appColors を使う |
カスタムクラスで Material の ColorScheme の役割を重複して定義する | Material でカバーされない値(例:成功、警告、情報)にのみ ThemeExtension トークンを作成する |
コールバックの型に dynamic または Object を使う | VoidCallback、ValueChanged<T>、または具体的な関数の型定義を使う |
| 内部実装ファイルを直接公開する | バレルファイルを使い、src/ 以下のすべてのファイルを非公開にする |
パッケージの作成
Very Good CLI MCP の create ツールを使ってひな形を作成します。
| パラメーター | 値 |
|---|---|
subcommand | app_ui_package — すべての UI パッケージで必ず指定します |
name | パッケージ名を snake_case で指定します(例:storefront_ui) |
output_directory | 共有パッケージを格納するモノレポのディレクトリ(例:packages/) |
app_ui_package は、lib/src/theme の拡張機能、バレルファイル、pumpApp ヘルパー、Widgetbook カタログを含むテンプレートです。flutter_package は、手作業で再構築するための空のパッケージです。
異なるハーネス環境での代替手段。 このプラグインの Bash フックがなく、Very Good CLI MCP サーバーにも接続されていないホストでは、同等の
very_good create app_ui_package <package_name> --output-directory <dir>コマンドを直接実行してください。
ThemeExtension クラスの一覧(AppColors、AppSpacing、AppTheme、BuildContext 拡張機能)、pumpApp テストヘルパー、バレルファイルの例、Widgetbook カタログの概念と build_runner コマンド、ウィジェットまたはカスタムトークンを追加する手順については、reference.md を参照してください。