preview-widget について
アプリ全体を起動せずにFlutterの単一ウィジェットをWidget Previewerで表示するスキルです。コンポーネントのデザインを反復調整する開発者向けに、プレビューサーバー管理、ヘッドレス撮影、既存プレビューの検索を行うスクリプトを備えています。
主な機能
- 01Flutterの対応バージョンを確認
- 02プレビューサーバーの起動と停止
- 03ヘッドレスChromeでプレビューを撮影
- 04プロジェクト内のプレビュー注釈を検索
- 05コンポーネントの複数バリエーションを表示
活用シーン
- 01単一ウィジェットの見た目を調整
- 02ライト、ダーク、タブレット表示を比較
- 03アプリ全体を起動せず描画を確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ chunkytofustudios/ flutter-skills --skill preview-widgetインストール後、Claude Code で/preview-widgetを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
assets/multi_preview.dart.templatepreview_wrapper.dart.templatereferences/limitations.mdpreview_annotations.mdwrapper_patterns.mdscripts/check_preview_support.shlist_previews.shscreenshot_preview.shstart_preview.shtests/check.batshelp.batshelpers.bashlist_previews.batsscreenshot.batsstart.batstests/stubs/chromeflutter詳細
Flutter ウィジェットを単独でプレビューする
Flutter の Widget Previewer でウィジェットを 1 つだけレンダリングし、アプリ全体を起動せずに、人と一緒にデザインの微調整、ライト/ダークのバリエーション、タブレット向けのサイズ調整などを繰り返せます。エージェントはライブプレビューをヘッドレスでスクリーンショットに撮影します。人は必要に応じて、同じ localhost URL を自身の Chrome で開いてプレビューを確認できます。
使用するタイミング
次の 3 つすべてに当てはまる場合に使用します。1 つでも当てはまらない場合は、このスキルは適していません。
- プロジェクトが Flutter プロジェクトである(作業ディレクトリに
pubspec.yamlがあり、lib/が存在する)。 - Flutter ≥ 3.35 が PATH 上にある(または
fvm flutterで利用できる)。プレビュー機能は stable では実験的ですが、デザインの検討には十分使えます。古い Flutter にはこの機能がありません。 - ユーザーが、画面全体やエンドツーエンドのフローではなく、単一のウィジェットを繰り返し調整したい。画面全体のビジュアル作業には
design-polishとandroid-emulatorを使用します。
セットアップ(呼び出し元のマシン)
| 要件 | 満たす方法 |
|---|---|
| Flutter ≥ 3.35 | bash scripts/check_preview_support.sh を実行します。バージョンが古い場合は、アップグレードを促すメッセージを表示して明確に失敗します。Flutter バイナリは FLUTTER_BIN=… で上書きできます(デフォルト:flutter、見つからない場合は fvm flutter にフォールバックします)。 |
| Google Chrome / Chromium | プレビューをキャプチャするために、screenshot_preview.sh がヘッドレスで使用します。macOS では /Applications/Google Chrome.app が自動的に検出されます。Linux では google-chrome、chromium、chromium-browser のいずれかが PATH 上にあれば使用できます。CHROME_BIN=/path/to/chrome で上書きできます。 |
bash 3.2 以降(macOS のデフォルト)、awk、grep、find | 標準で利用できます。Python や Node は不要です。 |
マシンごとの認証、アカウント、トークンは不要です。すべて localhost 上で動作します。
同梱スクリプト
scripts/check_preview_support.sh— Flutter ≥ 3.35 を確認し、解決されたバイナリとバージョンを表示します。終了コード 0 = 準備完了、終了コード 1 = バージョンが古い、終了コード 127 = Flutter が見つからない。scripts/start_preview.sh—flutter widget-preview startをバックグラウンドで起動し、ログから URL を解析して、小さな JSON 状態ファイルに書き込みます。冪等性があり、再実行すると既存の URL を返します。--stopでサーバーを停止します。デフォルトの状態ディレクトリ:./.preview-widget/(PREVIEW_WIDGET_STATE_DIRで上書きできます)。scripts/screenshot_preview.sh— 起動中のプレビューのスクリーンショットを、ヘッドレス Chrome で撮影します。フラグ:--out <path>、--size WxH(デフォルト1600x2400)、--wait <ms>(仮想時間の上限。デフォルト10000)、--url <url>(状態ファイルを上書きします)。標準出力に絶対パスを表示します。scripts/list_previews.sh—lib/内から@Preview(...)、@<Custom>Preview(...)、extends (Multi)?Previewの定義を検索し、すでに存在するものを再追加しないようにします。
4 つすべてのスクリプトで --help を利用できます。フラグを独自に指定する前に、ヘルプを確認してください。
Flutter プロジェクトごとの初回セットアップ
ウィジェットにアノテーションを付ける前に、プロジェクトにプレビューラッパーが必要です。これは、実際のウィジェットが前提とするグローバルな祖先チェーン(テーマキュービット/ProviderScope/MaterialApp/ローカライゼーション/MediaQuery のオーバーライドなど)を再現する、トップレベルの Widget previewWrapper(Widget child) 関数です。プレビューアの theme: パラメーターだけでは、単純でないアプリには不十分です。BlocBuilder<ThemeCubit, …> や Theme.of(context) を直接参照するウィジェットは、ツリーにキュービットがないと例外が発生するか、誤った表示になります。
手順:
- プロジェクトの
lib/main.dartとlib/app.dart(または同等のルートファイル)を読みます。MaterialAppをラップするすべての要素(MultiBlocProvider、ProviderScope、MultiRepositoryProvider、カスタムのInheritedWidget、MediaQueryのオーバーライド、ロケール/ルーターの設定)を特定します。 assets/preview_wrapper.dart.templateをlib/preview_wrapper.dartにコピーします。- インポートとプロバイダーリストを実際のルートツリーに合わせて記述し、実行時の状態が必要なプロバイダーはスタブ化します(例:
UxCubitにはデフォルトのDeviceType.mobileを設定し、リポジトリのキュービットには空の状態またはフェイクの状態を設定します)。ラッパーはコンパイル時の tearoff であるため、コールバックはすべてトップレベルの公開関数である必要があります。 - パターンの例(BLoC、Riverpod、GoRouter、Localizations、
MediaQuery、フォント)については、references/wrapper_patterns.mdを参照してください。
ウィジェットに必要なプロバイダーがわからない場合は、ウィジェット本体で Theme.of、BlocBuilder<、context.watch<、context.read<、ProviderScope.containerOf、MediaQuery.of、Localizations.of、GoRouter.of を検索してください。これらはすべて、不可欠な祖先要素です。
ウィジェットごとのワークフロー
1. アノテーション対象を選ぶ
@Preview と @MultiPreview を適用できるのは、次の対象です。
WidgetまたはWidgetBuilderを返すトップレベル関数- クラスの公開静的メソッド
- 必須引数のない公開ウィジェットコンストラクター/ファクトリー
アノテーションに渡す値はすべて const である必要があります。また、コールバック(wrapper:、theme:、localizations:)はすべて、公開されたトップレベル関数(または静的関数)である必要があります。プライベートな状態をキャプチャするクロージャーはプレビューアに拒否されます。
2. アノテーションを付ける
プロジェクトのラッパーを使用する単一プレビュー:
import 'package:flutter/material.dart';
import 'package:flutter/widget_previews.dart';
import '../preview_wrapper.dart';
@Preview(name: 'MyButton — default', wrapper: previewWrapper)
Widget myButtonPreview() => const MyButton();
同梱の assets/multi_preview.dart.template から lib/preview_variants.dart を作成し、ライト+ダーク+タブレットのマルチプレビューマトリックスを設定します。
@StandardPreview()
Widget myButtonPreview() => const MyButton();
@Preview のパラメーター一覧と対象の制限に関するルールは、references/preview_annotations.md を参照してください。
3. プレビューサーバーを起動する
bash scripts/start_preview.sh
# → Preview: http://localhost:51530
冪等です。サーバーが起動中に再実行すると、URL が再表示されるだけです。状態ファイル .preview-widget/server.json には { "url", "pid", "log" } が格納されます。.preview-widget/ を .gitignore に追加してください。
4. 実行中のプレビューをスクリーンショットに撮る
bash scripts/screenshot_preview.sh
# → /tmp/preview-widget/preview-001.png
次に、PNG を Read してライブプレビューを確認します。デフォルトのキャプチャサイズはビューポート 1600×2400 です(プレビュータイルを約 3 つ縦に並べて表示できます)。より縦長にキャプチャするには --size 1920x4000 を、保存先を指定するには --out /tmp/foo.png を渡します。最初の描画が空白の場合は、--wait 20000(20 秒)に増やしてください。これはキャンバス/Skia の起動に必要な時間です。
5. 繰り返す
edit Dart → sleep 2 → screenshot → read → reason → repeat
ファイルを保存するとプレビューアが自動でホットリスタートするため、手動での再起動は不要です。2 秒のスリープにより、次のスクリーンショットを撮る前にホットリスタートが完了します。
6. ターンの最後に必ず URL を出力する
プレビューアに関する操作を行ったアシスタントのメッセージはすべて、次の行で終わる必要があります。
Preview: http://localhost:<port>
これにより、人間はいつでも参加したり、後から戻ってきたりできます。サーバーは start_preview.sh --stop を実行するまで起動したままです。
7. 完了したらクリーンアップする
bash scripts/start_preview.sh --stop
サーバーを停止し、状態ファイルを削除します。省略可能です。セッション間も起動したままで問題ありません。
既存のプレビューを確認する
新しい @Preview を追加する前に、プロジェクト内にすでにあるプレビューを確認してください。
bash scripts/list_previews.sh
# lib/widgets/foo.dart:42 @Preview(name: 'Foo - light', wrapper: previewWrapper)
# lib/widgets/bar.dart:17 @StandardPreview()
# lib/preview_variants.dart:9 final class StandardPreview extends MultiPreview {
対象範囲を指定するには、明示的なルートを渡します:bash scripts/list_previews.sh lib/src/widgets。
注意点
- ラッパーが重要な役割を担います。
wrapper: previewWrapperを指定せずに単純な@Previewを使うと、ウィジェットのビルド時にプロバイダーやキュービットを読み込むアプリでは、クラッシュしたり正しく描画されなかったりします。ウィジェットがBlocBuilder<ThemeCubit>を使っていて、ラッパーがその上位にThemeCubitを配置していない場合、プレビューではProviderNotFoundExceptionが発生します。これはプレビューツールのバグではありません。 size:が制約するのはウィジェットであり、キャンバスではありません。size:を指定していないウィジェットは、制約がない場合、プレビューツールのキャンバスの約半分のサイズに自動調整されます。単一コンポーネントのプレビューでは、ほとんどの場合望ましくありません。ボタンサイズのコンポーネントにはsize: Size(360, 80)を、タブレットレイアウトにはsize: Size(1024, 1366)を指定してください。- アノテーションの値はすべて const である必要があります。
wrapper:、theme:、localizations:のコールバックも含まれます。ラムダではなく、トップレベル(または static)の公開関数への参照でなければなりません。外部の値をキャプチャすると、コンパイルに失敗します。 - プレビューするウィジェットでは、アセットパスに
packages/<name>/...を指定する必要があります。Image.asset('assets/foo.png')を使うウィジェットは実際のアプリでは動作しますが、プレビューツールでは動作しません。どちらの環境でも使えるように、Image.asset('packages/<your_pkg>/assets/foo.png')に変更してください。 dart:io、dart:ffi、ネイティブプラグインは使えません。 プレビューツールは Chrome で動作するため、File、Platform、ネイティブチャネル、プラットフォームプラグインにアクセスすると、プレビューの描画時にエラーが発生します。条件付きインポート(if (dart.library.html)など)で回避できます。- 初回の描画には時間がかかります。 初めてプレビューを読み込む際、Flutter の Web 環境が JS/Skia を起動するため、通常は 5~10 秒かかり、ディスクが遅い場合はさらに長くなります。デフォルトの
--wait 10000で通常は十分です。スクリーンショットの不具合を疑う前に、値を増やしてください(例:--wait 20000)。 - スクリーンショットはページ全体のみです。 プレビューツールはプレビューごとの URL を公開しないため、
screenshot_preview.shはページ全体(すべてのプレビュータイルを縦に並べたもの)をキャプチャします。エージェントは各タイルの上に表示されたname:ラベルをもとに、どのタイルがどれかを判断します。プレビューを 1 つだけ表示したい場合は、ほかのプレビューを一時的にコメントアウトするか、--sizeに縦長の値を指定し、group:パラメーターを使って関連するバリエーションを隣接させてください。 - ホットリスタートは自動ですが、即時ではありません。
.dartファイルを編集したら、スクリーンショットを撮る前にプレビューツールが反映するまで約 1~2 秒待ってください。ラッパー自体の編集は、反映にさらに時間がかかります。sleep 5の後もスクリーンショットに古い状態が表示される場合は、サーバーを再起動してください(--stopを実行してから、再実行します)。 - サーバーごとにプロジェクトは 1 つです。 Flutter 3.35 の時点では、プレビューツールは IDE の操作フローで複数プロジェクトのワークスペースをサポートしていません。CLI サーバーが認識するのは現在のプロジェクトのみです。プロジェクトで Pub ワークスペースを使っている場合は、ワークスペースのルートからサーバーを起動してください。
flutter widget-previewは実験的機能です。 API や CLI フラグは、Flutter のバージョンによって変更される可能性があります。このスキルで使っているフラグが動作しなくなった場合は、flutter widget-preview --helpの内容とstart_preview.shが渡す引数を確認してください。
クイックリファレンス
# 0. (one-time) Verify Flutter is recent enough.
bash skills/preview-widget/scripts/check_preview_support.sh
# 1. (one-time per project) Copy the wrapper template, fill in providers.
cp skills/preview-widget/assets/preview_wrapper.dart.template lib/preview_wrapper.dart
# …edit lib/preview_wrapper.dart, then add `.preview-widget/` to .gitignore
# 2. Annotate the widget (in its own file or alongside it).
# @Preview(name: 'MyWidget', wrapper: previewWrapper)
# Widget myWidgetPreview() => const MyWidget();
# 3. Start server, capture URL.
bash skills/preview-widget/scripts/start_preview.sh
# → http://localhost:51530
# 4. Iterate: edit → sleep 2 → screenshot → read → repeat.
bash skills/preview-widget/scripts/screenshot_preview.sh
# 5. Stop when done.
bash skills/preview-widget/scripts/start_preview.sh --stop
プレビューツールに関わる操作を行ったアシスタントの各応答の最後には、Preview: http://localhost:<port> を記載して、人がプレビューを確認できるようにしてください。