SKILL.md
唯讀
名稱
flutter-fix-layout-issues
描述
使用 Dart 與 Flutter MCP 工具修復 Flutter 的排版錯誤(如內容溢位、無邊界約束問題)。當遇到「RenderFlex overflowed」、「Vertical viewport was given unbounded height」或類似的版面配置問題時使用。
解決 Flutter 版面配置錯誤
目錄
約束違規診斷
Flutter 的排版運作遵循一條嚴格的原則:約束向下傳遞,尺寸向上回傳,父元件決定位置(Constraints go down. Sizes go up. Parent sets position.)。當這套協商機制失敗時(通常是因為無邊界約束或未受約束的子元件),就會引發排版錯誤。
你可以透過以下錯誤特徵來診斷排版問題:
- "Vertical viewport was given unbounded height":當可滾動元件(
ListView、GridView)放置在未限制高度的垂直父元件(Column)內部時觸發。父元件提供了無限高度,而子元件嘗試無限延伸。 - "An InputDecorator...cannot have an unbounded width":當
TextField或TextFormField放置在未限制寬度的水平父元件(Row)內部時觸發。文字輸入框嘗試在無限可用空間中計算自身的寬度。 - "RenderFlex overflowed":當
Row或Column的子元件要求的尺寸超過父元件所分配的約束時觸發。畫面上會以黃黑相間的警告條紋顯示。 - "Incorrect use of ParentData widget":當
ParentDataWidget不是其所要求的祖先元件的直接子代時觸發(例如在Flex外部使用Expanded,或在Stack外部使用Positioned)。 - "RenderBox was not laid out":此為連帶引發的次要錯誤。請忽略此訊息,並往堆疊追蹤(stack trace)上方尋找主要的約束違規問題(通常是高度/寬度無邊界錯誤)。
排版錯誤解決流程
複製並使用以下檢查清單,系統化地解決排版約束違規問題。
任務進度
- [ ] 在偵錯模式(debug mode)下執行應用程式,以在主控台(console)擷取確切的排版異常訊息。
- [ ] 找出主要錯誤訊息(忽略連帶產生的 "RenderBox was not laid out" 錯誤)。
- [ ] 根據具體錯誤類型採取對應的修復方式:
- 若為 "Vertical viewport was given unbounded height":將可滾動的子元件(
ListView、GridView)包裹在Expanded元件中以佔滿剩餘空間,或包裹在SizedBox中以提供明確的高度約束。 - 若為 "An InputDecorator...cannot have an unbounded width":將
TextField或TextFormField包裹在Expanded或Flexible元件中。 - 若為 "RenderFlex overflowed":限制溢位的子元件,將其包裹在
Expanded元件中(強制符合可用空間),或包裹在Flexible元件中(允許縮小至小於分配空間)。 - 若為 "Incorrect use of ParentData widget":將
ParentDataWidget移動至其所要求之父元件的直接子代。確保Expanded/Flexible是Row/Column/Flex的直接子元件;確保Positioned是Stack的直接子元件。
- 若為 "Vertical viewport was given unbounded height":將可滾動的子元件(
- [ ] 執行 Flutter 熱重載(hot reload)。
- [ ] 執行驗證 -> 檢視錯誤 -> 修復:檢查 UI 以確認紅色/灰色錯誤畫面或黃黑相間的溢位條紋已消失。若出現新的排版錯誤,請重複執行此流程。
範例
修復無邊界高度問題(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')),
],
),
],
)
輸出(已解決狀態):
// 將 ListView 包裹在 Expanded 中,將其高度限制在 Column 的剩餘空間內
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(),
],
)
輸出(已解決狀態):
// 將 TextField 包裹在 Expanded 中,將其寬度限制在 Row 的剩餘空間內
Row(
children: [
const Icon(Icons.search),
Expanded(
child: TextField(),
),
],
)
修復 RenderFlex 溢位問題
輸入(錯誤狀態):
// 拋出 "A RenderFlex overflowed by X pixels on the right"
Row(
children: [
const Icon(Icons.info),
const Text('This is a very long text string that will definitely overflow the available screen width and cause a RenderFlex error.'),
],
)
輸出(已解決狀態):
// 將 Text 元件包裹在 Expanded 中,強制其在可用約束內換行
Row(
children: [
const Icon(Icons.info),
Expanded(
child: const Text('This is a very long text string that will definitely overflow the available screen width and cause a RenderFlex error.'),
),
],
)




