flutter-fix-layout-issues について
flutter-fix-layout-issuesは、Flutterのレイアウトエラーの診断と修正を支援するスキルです。一般的なエラーメッセージを制約の原因と対処法に結び付け、修正を確認する手順も示します。
主な機能
- 01エラーの特徴からレイアウト原因を特定
- 02Flutterの制約交渉モデルを説明
- 03Expandedなどを使う修正パターン
- 04主原因と連鎖エラーの区別
- 05一般的な修正例の提示
活用シーン
- 01Column内のListViewの高さ制約を修正
- 02Row内のTextFieldの幅制約を解消
- 03RenderFlexのオーバーフローに対処
- 04ParentDataウィジェットの配置を修正
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ dhruvanbhalara/ skills --skill flutter-fix-layout-issuesインストール後、Claude Code で/flutter-fix-layout-issuesを入力して読み込まれたか確認します。
詳細
目次
制約モデル
Flutter のレイアウトは、厳格なネゴシエーションルールに基づいて動作します。
制約は下へ。サイズは上へ。親が位置を決定する。
- 親ウィジェットは制約(最小/最大の幅と高さ)を子に渡します。
- 子はその制約内で自身のサイズを決定します。
- 親は子の位置を決定します。
レイアウトエラーは、このネゴシエーションが失敗したときに発生します(typically when a parent provides unbounded constraints and the child attempts to expand infinitely)。
エラーシグネチャカタログ
| エラーメッセージ | 根本原因 | クイックフィックス |
|---|---|---|
Vertical viewport was given unbounded height | 制約のない垂直方向の親(Column)内のスクロール可能なウィジェット(ListView、GridView) | Expanded または SizedBox(height: ...) でラップする |
An InputDecorator...cannot have an unbounded width | 制約のない水平方向の親(Row)内の TextField | Expanded でラップする |
A RenderFlex overflowed by X pixels | 子が親の割り当てられた制約を超えている | Expanded や Flexible でラップする、または overflow: TextOverflow.ellipsis を使用する |
Incorrect use of ParentData widget | Flex の外にある Expanded、Stack の外にある Positioned | ウィジェットを正しい親の直接の子になるように移動する |
RenderBox was not laid out | 連鎖的なエラー: スタックトレースの上流を確認する | その上にある主要な制約エラーを修正する |
ルール: スタックトレース内の最初のエラーを常に修正してください。RenderBox was not laid out は、ほとんどの場合、連鎖的に発生する副作用です。
解決パターン
デシジョンツリー
Error detected
├── Contains "unbounded height"?
│ └── Wrap scrollable child in Expanded or SizedBox
├── Contains "unbounded width"?
│ └── Wrap TextField/InputDecorator in Expanded
├── Contains "RenderFlex overflowed"?
│ ├── Text overflow?
│ │ └── Add overflow: TextOverflow.ellipsis + Expanded wrapper
│ └── Widget overflow?
│ └── Wrap in Expanded or Flexible
├── Contains "ParentData"?
│ └── Ensure Expanded is direct child of Row/Column/Flex
│ Ensure Positioned is direct child of Stack
└── Contains "RenderBox was not laid out"?
└── IGNORE: fix the error above this one
Expanded vs Flexible vs SizedBox
| ウィジェット | 動作 | 使用すべき場合 |
|---|---|---|
Expanded | 子に残りのすべてのスペースを埋めることを強制する | 子を引き伸ばして埋めるべき場合 |
Flexible | 子が残りのスペースより小さくなることを許可する | 子が自然なサイズを持つが、オーバーフローすべきでない場合 |
SizedBox | 絶対的な固定制約を提供する | 必要な正確な寸法がわかっている場合 |
ConstrainedBox | 最小/最大の制約を設定する | 境界のある柔軟性が必要な場合 |
ワークフロー: レイアウト問題の修正
タスクの進捗
- ステップ 1: デバッグモードでアプリを実行する: コンソールで正確な例外をキャプチャします。
- ステップ 2: 主要なエラーメッセージを特定します(連鎖的な
RenderBox was not laid outは無視します)。 - ステップ 3: 上記のエラーシグネチャカタログとエラーを照合します。
- ステップ 4: 条件に応じた修正を適用します:
unbounded heightの場合 → スクロール可能なウィジェットをExpandedまたはSizedBoxでラップするunbounded widthの場合 → 入力をExpandedでラップするRenderFlex overflowedの場合 →ExpandedまたはFlexibleでラップするParentDataの場合 → ウィジェットツリーを再構築する
- ステップ 5: ホットリロード → エラーが解決されたことを確認します。
- ステップ 6: 新しいレイアウトエラーが発生した場合 → ステップ 2 から繰り返します。
例
制約のない高さの修正(Column 内の ListView)
修正前(Vertical viewport was given unbounded height をスローする):
Column(
children: <Widget>[
const Text('Header'),
ListView(
children: const <Widget>[
ListTile(title: Text('Item 1')),
ListTile(title: Text('Item 2')),
],
),
],
)
修正後(解決済み):
Column(
children: <Widget>[
const Text('Header'),
Expanded(
child: ListView(
children: const <Widget>[
ListTile(title: Text('Item 1')),
ListTile(title: Text('Item 2')),
],
),
),
],
)
制約のない幅の修正(Row 内の TextField)
修正前(An InputDecorator...cannot have an unbounded width をスローする):
Row(
children: [
const Icon(Icons.search),
TextField(),
],
)
修正後(解決済み):
Row(
children: [
const Icon(Icons.search),
Expanded(
child: TextField(),
),
],
)
RenderFlex オーバーフローの修正(Row 内の Text)
修正前(A RenderFlex overflowed by X pixels on the right をスローする):
Row(
children: [
const Icon(Icons.info),
Text('This is a very long text that will overflow the screen width'),
],
)
修正後(解決済み):
Row(
children: [
const Icon(Icons.info),
Expanded(
child: Text(
'This is a very long text that will overflow the screen width',
overflow: TextOverflow.ellipsis,
),
),
],
)
ParentData の誤用の修正
修正前(Incorrect use of ParentData widget をスローする):
// Expanded must be a DIRECT child of Row/Column/Flex
Container(
child: Expanded( // WRONG: Expanded not inside a Flex
child: Text('Hello'),
),
)
修正後(解決済み):
Row(
children: [
Expanded( // OK: Direct child of Row (a Flex widget)
child: Text('Hello'),
),
],
)
コメント
Markdown 対応