はじめに

Flutter公式のAIエージェントプラグインが公開されました。
これはClaude等に対してFlutter開発の作法を与えるもので、中身は大きく Agent Skills と Dart MCP Server の2つです。
本記事では、Agent Skills と Dart MCP Server を組み合わせて、コードが定義された手順どおりに修正されるか検証を行います。

前提条件

検証を行った環境は下記になります。

  • Fvm ver 4.0.5
  • Flutter ver 3.38.3
  • Dart ver 3.10.1
  • Claude 2.1.268

AIプラグインの導入手順

1. プラグイン導入

claude plugin marketplace add flutter/agent-plugins
claude plugin install dart-flutter@dart-flutter

2. プラグイン導入確認

claude plugin marketplace list

下記のような表示になっていれば正常に導入されています。
スキルが24本(Flutter系10 + Dart系14)、MCPサーバーが1つ登録されている事がわかります。

Dart and Flutter (dart-flutter) 1.0.3
... 省略

Component inventory
  Skills (24)  dart-add-unit-test, ...省略 , flutter-setup-declarative-routing, ... 省略
  Agents (0)
  Hooks (0)
  MCP servers (1)  dart-mcp-server  (tool schemas resolved at runtime; not counted)
  LSP servers (0)

プラグインの導入手順としては以上になります。

検証コードの用意

本記事では、導入されたスキルのうち flutter-fix-layout-issues を検証します。

このスキルは MCP ツールを活用してレイアウトエラーを診断し、修正してホットリロードで再検証するまでを手順に含んでいます。識別するエラーは5種類で、うち4種類に修正方法が割り当てられています。

  • エラー内容: “Vertical viewport was given unbounded height”
    • 修正方法: Expandedで囲む / SizedBoxで高さを与える
  • エラー内容: “An InputDecorator…cannot have an unbounded width”
    • 修正方法: ExpandedかFlexibleで囲む
  • エラー内容: “RenderFlex overflowed”
    • 修正方法: ExpandedかFlexibleで囲む
  • エラー内容: “Incorrect use of ParentData widget”
    • 修正内容: ParentDataWidgetを必要な親の直接の子に移す
  • エラー内容: “RenderBox was not laid out”
    • 修正内容: 連鎖的エラーの為、根本的なエラー箇所を探す

今回はRenderFlex overflowedの検証を行います。
RenderFlex overflowedの詳細とスキル指定の修正方法は以下の通りです。

RenderFlex overflowedとは

子のWidgetが親のWidgetから割り当てられた制約よりも大きなサイズを要求した場合に発生するエラーになります。RowやColumnで発生し、アプリ画面上で警告ストライプが表示されます。

スキル指定の修正方法

スキル指定の修正方法は下記2パターンです

  • Expandedで子Widgetを囲む: 割り当てられた空間一杯に子Widgetを広げます
  • Flexibleで子Widgetを囲む: 割り当てられた空間を上限に、必要な分だけ子Widgetに使わせる

どちらを選ぶかはエージェント次第になります。

検証コード

class SampleApp extends StatelessWidget {
  const SampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Overflow',
      home: Scaffold(
        appBar: AppBar(title: const Text('Overflow')),
        body: const Center(
          child: SizedBox(
            width: 200,
            child: Row(
              children: const [
                Icon(Icons.info_outline),
                Text('レイアウト制約の検証に使用する長いテキストです'),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

検証手順

MCP登録

Claude Codeに対してプロジェクト専用の MCP サーバーとしてdart-mcp-serverを登録する。

claude mcp add dart-mcp-server -s project -- fvm dart mcp-server

プロジェクトルートには下記のような.mcp.jsonが生成されます。

{
  "mcpServers": {
    "dart-mcp-server": {
      "command": "fvm",
      "args": ["dart", "mcp-server"],
      "env": {}
    }
  }
}

この設定により、claudeを起動してから/exitするまでのセッション間でMCPサーバーのプロセスが起動したままになります。
Claude Codeはスキルの手順に則って、MCPサーバーに対し情報の取得や操作を要求することになります。

セッション起動

claude

MCPサーバー承認

下記のような表示がされので、一番上を選択します。

MCP servers may execute code or access system resources.

    Use this MCP server
    Use this and all future MCP servers in this project
  ❯ Continue without using this MCP server

MCPサーバー接続確認

下記のような表示になっていれば問題ありません。

   Manage MCP servers
   2 servers

     Project MCPs (---/.mcp.json)
   ❯ dart-mcp-server · ✔ connected · 24 tools

     Built-in MCPs (always available)
     plugin:dart-flutter:dart-mcp-server · ◯ disabled

MCPが正常に動作するかの確認

実際にClaude CodeがMCPツールを使用できているか確認します。
下記のようなプロンプトをClaude Codeに投げてみます。

launch_appで macOS ターゲットとしてこのアプリを起動してください。起動したら
get_runtime_errorsでオーバーフローのエラーを取得し、
widget_inspectorでSampleAppのウィジェットツリーと実際の制約を見せてください。修正はまだしないでください。
launch_app / get_runtime_errors / widget_inspectorはそれぞれdart_mcp_serverのツールになります。
上記を入力するとアプリが自動で起動し、結果として下記のようなものが出力されます。
1. ランタイムエラー(get_runtime_errors)

エラー1件:

══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═══════════════════════
A RenderFlex overflowed by 151 pixels on the right.

The relevant error-causing widget was:
  Row  lib/main.dart:17:46

- 方向: Axis.horizontal
- ヒント: Expanded などの flex factor を当てるか、ClipRect / スクロール可能なコンテナを検討せよ

2. ウィジェットツリー(widget_inspector, summaryOnly)

RootWidget
└─ SampleApp                      lib/main.dart:7
   └─ MaterialApp
      └─ Scaffold
         ├─ AppBar
         │  └─ Text "Overflow"
         └─ Center
            └─ SizedBox (width: 200)
               └─ Row
                  ├─ Icon (Icons.info_outline)
                  └─ Text "レイアウト制約の検証に使用する長いテキストです"

3. 実際の制約とサイズ

┌──────────────────────────────────────┬─────────────────────────────────────────────────────────────┬────────────────────────────────────┐
│                ノード                │                         constraints                         │                size                │
├──────────────────────────────────────┼─────────────────────────────────────────────────────────────┼────────────────────────────────────┤
│ RenderFlex (Row)                     │ BoxConstraints(w=200.0, 0.0<=h<=544.0) — 幅は 200 に固定    │ Size(200.0, 24.0)                  │
├──────────────────────────────────────┼─────────────────────────────────────────────────────────────┼────────────────────────────────────┤
│ child 1: Icon (RenderConstrainedBox) │ 0.0<=w<=Infinity, 0.0<=h<=544.0 → additional w=24.0, h=24.0 │ Size(24.0, 24.0) @ offset (0, 0)   │
├──────────────────────────────────────┼─────────────────────────────────────────────────────────────┼────────────────────────────────────┤
│ child 2: Text (RenderParagraph)      │ 0.0<=w<=Infinity, 0.0<=h<=544.0 — 上限なし                  │ Size(326.9, 20.0) @ offset (24, 2) │
└──────────────────────────────────────┴─────────────────────────────────────────────────────────────┴────────────────────────────────────┘

Row の mainAxisSize: max, crossAxisAlignment: center, flex 指定は両子とも flex=null; fit=null。

4. 何が起きているか

SizedBox(width: 200) が Row に「幅ちょうど 200」のタイト制約を渡している一方、Row は flex を持たない子に対して主軸方向を無制限(0.0<=w<=Infinity)で測ります。そのため Text は自然幅 326.9px でレイアウトされ、Icon 24.0 + Text 326.9 = 350.9px に対し使える幅は 200px。差分の 150.9 ≒ 151px がそのままオーバーフロー量になっています。softWrap: true ですが、幅上限が Infinity のため折り返し位置が決まらず 1 行のままです。

修正案が必要であれば指示してください(Expanded/Flexible で折り返す、maxLines+ellipsis、SizedBox の幅を広げる等)。アプリは起動したままにしてあります。

実際にスキルによって検証コードを修正する

flutter-fix-layout-issuesスキルを使用して、実際にコードを修正します。
下記のようなプロンプトをClaude Codeに投げます。

SampleAppのオーバーフローエラーを直してください。
どのMCPツールとスキルを使用したかも教えてください。
結果は下記の通りになります。
原因

lib/main.dart:17 の Row が、幅200pxの SizedBox に入っているのに、子の Text が制約なしで自然幅のまま配置されていました。

- Text の自然幅: 326.9px
- Icon が24px使うので、残りは 176px
- → A RenderFlex overflowed by 151 pixels on the right.

修正

Text を Expanded で包み、残り幅に収まるよう折り返すようにしました。ついでに1行に詰め込まれていた記述を整形しています。

child: SizedBox(
  width: 200,
  child: Row(
    children: [
      Icon(Icons.info_outline),
      Expanded(
        child: Text('レイアウト制約の検証に使用する長いテキストです'),
      ),
    ],
  ),
),

検証

- 修正前: macOSで実行し get_runtime_errors でオーバーフロー例外を実際に確認
- 修正後: ホットリロード → get_runtime_errors が No runtime errors found
- analyze_files も No errors
- 確認後、アプリは停止しました

使用したツール / スキル

スキル
- dart-flutter:flutter-fix-layout-issues — レイアウト制約エラーの診断・修正ワークフロー。「RenderFlex overflowed の場合は Expanded / Flexible で制約する」という指針に従いました

MCPツール(dart-mcp-server)

┌─────────────────────────────┬────────────────────────────────────────────┐
│           ツール            │                    用途                    │
├─────────────────────────────┼────────────────────────────────────────────┤
│ list_running_apps           │ 起動中アプリの確認                         │
├─────────────────────────────┼────────────────────────────────────────────┤
│ list_devices                │ デバイス一覧の取得                         │
├─────────────────────────────┼────────────────────────────────────────────┤
│ launch_app                  │ macOSでデバッグ起動                        │
├─────────────────────────────┼────────────────────────────────────────────┤
│ connect_dart_tooling_daemon │ DTDへ接続                                  │
├─────────────────────────────┼────────────────────────────────────────────┤
│ get_runtime_errors          │ オーバーフロー例外の再現確認と修正後の検証 │
├─────────────────────────────┼────────────────────────────────────────────┤
│ hot_reload                  │ 修正の反映                                 │
├─────────────────────────────┼────────────────────────────────────────────┤
│ analyze_files               │ 静的解析                                   │
├─────────────────────────────┼────────────────────────────────────────────┤
│ stop_app                    │ アプリ停止                                 │
└─────────────────────────────┴────────────────────────────────────────────┘

スキル、MCPツール、それぞれ意図したものが使われている事がわかると思います。
実際の検証コードも適切に修正されていました。

まとめ

Flutter AIプラグインを使用する事で得られるのは、 Flutterチームが推奨する手順(Skill)の固定とプロジェクトの状態を観測する手段(MCP)の2つになります。
これらによりAIエージェントは実行中のアプリから得た実測値を元に、固定化された手順でアプリのコードを修正及び検証までを自律的に行えるようになります。

参考

https://docs.flutter.dev/ai/tools

https://github.com/flutter/agent-plugins/tree/main/skills

https://pub.dev/packages/dart_mcp_server