SKILL.md
唯讀
名稱
flutter-build-responsive-layout
描述
使用 `LayoutBuilder`、`MediaQuery` 或 `Expanded/Flexible` 打造能適應不同螢幕尺寸的版面配置。當你需要讓 UI 在手機、平板及電腦等各種裝置型態上都能展現良好視覺效果時使用。
實作自適應版面配置
目錄
空間量測指南
精確測量可用空間,確保版面配置是依據「應用程式視窗」而非僅依據「實體裝置」進行自適應調整。
- 使用
MediaQuery.sizeOf(context)來取得整個應用程式視窗的尺寸。 - 使用
LayoutBuilder根據父階 Widget 所分配到的空間來決定版面配置。評估constraints.maxWidth以決定要回傳哪個合適的 Widget 樹。 - 切勿在 Widget 樹頂層附近使用
MediaQuery.orientationOf或OrientationBuilder來切換版面。裝置螢幕方向無法準確反映應用程式視窗實際可用的空間。 - 請勿檢查硬體類型(例如區分「手機」或「平板」)。Flutter 應用程式可能會運行於可調整大小的視窗、多視窗模式或子母畫面(Picture-in-Picture)中。請嚴格依據可用的視窗空間來做出所有版面決策。
Widget 尺寸與約束條件
理解並運用 Flutter 的核心版面配置規則:約束往下傳,尺寸往上報,父階決定位置(Constraints go down. Sizes go up. Parent sets position.)。
- 分配空間: 在
Row、Column或Flex中使用Expanded與Flexible。- 使用
Expanded強制子元件填滿所有剩餘可用空間(相當於設定fit: FlexFit.tight且flex為 1.0 的Flexible)。 - 使用
Flexible允許子元件在特定上限內自行決定尺寸,同時仍保有伸縮能力。使用flex權重係數來定義同階(sibling)元件之間劃分空間的比例。
- 使用
- 限制寬度: 避免 Widget 在大螢幕上佔滿所有水平空間。將
GridView或ListView等 Widget 包裹在ConstrainedBox或Container中,並於BoxConstraints中設定maxWidth。 - 延遲渲染(Lazy Rendering): 當渲染數量未知或大量的列表項目時,務必使用
ListView.builder或GridView.builder。
裝置與螢幕轉向行為
確保應用程式在各種裝置型態及輸入方式下皆能正常運作。
- 請勿鎖定螢幕方向。 鎖定方向會在摺疊螢幕裝置上引發嚴重的版面配置問題,通常會導致畫面出現黑邊(letterboxing,即應用程式置中且四周留黑)。Android 大螢幕等級規範要求同時支援直向與橫向模式。
- 鎖定方向時的備用方案: 若業務需求強制要求鎖定方向,請使用
Display API取得實體螢幕尺寸,而非使用MediaQuery。在相容性模式下,MediaQuery無法取得較大的視窗尺寸。 - 支援多種輸入方式: 實作對基礎滑鼠、觸控板及鍵盤快捷鍵的支援。確保觸控標的(touch targets)尺寸合宜,並提供良好的鍵盤導覽存取性(accessibility)。
工作流程:構建自適應版面
請遵循此工作流程來實作能自動適應可用 BoxConstraints 的版面配置。
任務進度:
- [ ] 確認需要具備自適應行為的目標 Widget。
- [ ] 將 Widget 樹包裹在
LayoutBuilder中。 - [ ] 從 builder 回呼函式中提取
constraints.maxWidth。 - [ ] 定義自適應斷點(例如
largeScreenMinWidth = 600)。 - [ ] 若
maxWidth > largeScreenMinWidth: 回傳大螢幕版面(例如使用Row將導覽側邊欄與內容區域並排呈現)。 - [ ] 若
maxWidth <= largeScreenMinWidth: 回傳小螢幕版面(例如使用Column或標準的導覽頁面結構)。 - [ ] 執行驗證器 -> 調整應用程式視窗大小 -> 檢視版面過渡效果 -> 修復溢出(overflow)錯誤。
工作流程:針對大螢幕進行最佳化
請遵循此工作流程,防止 UI 元素在大螢幕顯示器上發生不自然的拉伸。
任務進度:
- [ ] 找出全寬組件(例如
ListView、文字區塊、表單)。 - [ ] 若要最佳化列表: 將
ListView.builder轉換為GridView.builder,並配合SliverGridDelegateWithMaxCrossAxisExtent根據視窗大小自動調整欄數。 - [ ] 若要最佳化表單或文字區塊: 將組件包裹在
ConstrainedBox中。 - [ ] 為
ConstrainedBox套用BoxConstraints(maxWidth: [optimal_width])。 - [ ] 將
ConstrainedBox包裹在CenterWidget 中,使受限內容在大螢幕上保持置中。 - [ ] 執行驗證器 -> 在桌面端/平板目標上測試 -> 檢視水平拉伸情況 -> 調整
maxWidth或網格範圍(grid extents)。
程式碼範例
使用 LayoutBuilder 的自適應版面
示範如何根據可用寬度在手機與桌面版面之間進行切換。
import 'package:flutter/material.dart';
const double largeScreenMinWidth = 600.0;
class AdaptiveLayout extends StatelessWidget {
const AdaptiveLayout({super.key});
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth > largeScreenMinWidth) {
return _buildLargeScreenLayout();
} else {
return _buildSmallScreenLayout();
}
},
);
}
Widget _buildLargeScreenLayout() {
return Row(
children: [
const SizedBox(width: 250, child: Placeholder(color: Colors.blue)),
const VerticalDivider(width: 1),
Expanded(child: const Placeholder(color: Colors.green)),
],
);
}
Widget _buildSmallScreenLayout() {
return const Placeholder(color: Colors.green);
}
}
在大螢幕上限制寬度
示範如何防止 Widget 佔滿所有水平空間。
import 'package:flutter/material.dart';
class ConstrainedContent extends StatelessWidget {
const ConstrainedContent({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: 800.0, // 適合閱讀的最大寬度
),
child: ListView.builder(
itemCount: 50,
itemBuilder: (context, index) {
return ListTile(
title: Text('Item $index'),
);
},
),
),
),
);
}
}






