SKILL.md
只读
名称
flutter-build-responsive-layout
描述
使用 `LayoutBuilder`、`MediaQuery` 或 `Expanded/Flexible` 创建适应不同屏幕尺寸的布局。当您需要 UI 在移动端和平板/桌面端形态下都看起来良好时使用。
实现自适应布局
目录
空间测量指南
准确确定可用空间,确保布局适应应用窗口,而非仅物理设备。
- 使用
MediaQuery.sizeOf(context)获取整个应用窗口的大小。 - 使用
LayoutBuilder根据父组件分配的空间做出布局决策。评估constraints.maxWidth以确定返回合适的组件树。 - 不要在组件树顶层使用
MediaQuery.orientationOf或OrientationBuilder来切换布局。设备方向不能准确反映应用窗口的可用空间。 - 不要检查硬件类型(例如“手机”与“平板”)。Flutter 应用运行在可调整大小的窗口、多窗口模式和画中画模式下。所有布局决策应严格基于可用窗口空间。
组件尺寸与约束
理解并应用 Flutter 的核心布局规则:约束向下传递。尺寸向上传递。父组件设置位置。
- 分配空间: 在
Row、Column或Flex组件中使用Expanded和Flexible。- 使用
Expanded强制子组件填满所有剩余可用空间(等同于flex因子为 1.0 且fit: FlexFit.tight的Flexible)。 - 使用
Flexible允许子组件在扩展/收缩的同时,将自身大小限制在特定范围内。使用flex因子定义兄弟组件间的空间消耗比例。
- 使用
- 限制宽度: 防止组件在大屏幕上占用所有水平空间。将
GridView或ListView等组件包裹在ConstrainedBox或Container中,并在BoxConstraints中定义maxWidth。 - 懒加载: 当渲染未知数量或大量列表项时,始终使用
ListView.builder或GridView.builder。
设备与方向行为
确保应用在所有设备形态和输入方式下行为正确。
- 不要锁定屏幕方向。 锁定方向会在可折叠设备上导致严重的布局问题,通常会出现黑边(应用居中并带有黑色边框)。Android 大屏层级要求同时支持竖屏和横屏。
- 锁定方向的回退方案: 如果业务需求强制要求锁定方向,请使用
Display API获取物理屏幕尺寸,而不是MediaQuery。MediaQuery在兼容模式下无法获取更大的窗口尺寸。 - 支持多种输入: 实现对基本鼠标、触控板和键盘快捷键的支持。确保触摸目标尺寸合适,键盘导航可访问。
工作流:构建自适应布局
按照以下工作流实现适应可用 BoxConstraints 的布局。
任务进度:
- [ ] 确定需要自适应行为的目标组件。
- [ ] 将组件树包裹在
LayoutBuilder中。 - [ ] 从 builder 回调中提取
constraints.maxWidth。 - [ ] 定义自适应断点(例如
largeScreenMinWidth = 600)。 - [ ] 如果
maxWidth > largeScreenMinWidth: 返回大屏布局(例如,使用Row将导航侧边栏和内容区域并排放置)。 - [ ] 如果
maxWidth <= largeScreenMinWidth: 返回小屏布局(例如,使用Column或标准导航方式)。 - [ ] 运行验证器 -> 调整应用窗口大小 -> 检查布局过渡 -> 修复溢出错误。
工作流:针对大屏优化
按照以下工作流防止 UI 元素在大屏幕上过度拉伸。
任务进度:
- [ ] 识别全宽组件(例如
ListView、文本块、表单)。 - [ ] 如果优化列表: 将
ListView.builder转换为使用SliverGridDelegateWithMaxCrossAxisExtent的GridView.builder,以根据窗口大小自动调整列数。 - [ ] 如果优化表单或文本块: 将组件包裹在
ConstrainedBox中。 - [ ] 对
ConstrainedBox应用BoxConstraints(maxWidth: [optimal_width])。 - [ ] 将
ConstrainedBox包裹在Center组件中,使受限内容在大屏幕上居中显示。 - [ ] 运行验证器 -> 在桌面/平板目标上测试 -> 检查水平拉伸 -> 调整
maxWidth或网格范围。
示例
使用 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);
}
}
在大屏幕上限制宽度
演示防止组件占用所有水平空间。
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'),
);
},
),
),
),
);
}
}






