flutter-build-responsive-layout
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseImplementing Adaptive Layouts
实现自适应布局
Contents
目录
Space Measurement Guidelines
空间测量准则
Determine the available space accurately to ensure layouts adapt to the app window, not just the physical device.
- Use to get the size of the entire app window.
MediaQuery.sizeOf(context) - Use to make layout decisions based on the parent widget's allocated space. Evaluate
LayoutBuilderto determine the appropriate widget tree to return.constraints.maxWidth - Do not use or
MediaQuery.orientationOfnear the top of the widget tree to switch layouts. Device orientation does not accurately reflect the available app window space.OrientationBuilder - Do not check for hardware types (e.g., "phone" vs. "tablet"). Flutter apps run in resizable windows, multi-window modes, and picture-in-picture. Base all layout decisions strictly on available window space.
准确确定可用空间,确保布局适配应用窗口,而非仅适配物理设备。
- **使用**获取整个应用窗口的尺寸。
MediaQuery.sizeOf(context) - **使用**基于父组件分配的空间做出布局决策。通过评估
LayoutBuilder来确定应返回的组件树。constraints.maxWidth - **不要在组件树顶部附近使用或
MediaQuery.orientationOf**来切换布局。设备方向无法准确反映应用窗口的可用空间。OrientationBuilder - 不要检测硬件类型(例如“手机” vs “平板”)。Flutter应用可在可调整大小的窗口、多窗口模式和画中画模式下运行。所有布局决策都应严格基于窗口可用空间。
Widget Sizing and Constraints
组件尺寸与约束
Understand and apply Flutter's core layout rule: Constraints go down. Sizes go up. Parent sets position.
- Distribute Space: Use and
ExpandedwithinFlexible,Row, orColumnwidgets.Flex- Use to force a child to fill all remaining available space (equivalent to
ExpandedwithFlexibleand afit: FlexFit.tightfactor of 1.0).flex - Use to allow a child to size itself up to a specific limit while still expanding/contracting. Use the
Flexiblefactor to define the ratio of space consumption among siblings.flex
- Use
- Constrain Width: Prevent widgets from consuming all horizontal space on large screens. Wrap widgets like or
GridViewin aListVieworConstrainedBoxand define aContainerin themaxWidth.BoxConstraints - Lazy Rendering: Always use or
ListView.builderwhen rendering lists with an unknown or large number of items.GridView.builder
理解并应用Flutter的核心布局规则:约束向下传递,尺寸向上反馈,父组件设置位置。
- **空间分配:**在、
Row或Column组件中使用Flex和Expanded。Flexible- 使用强制子组件填充所有剩余可用空间(等同于
Expanded且fit: FlexFit.tight因子为1.0的flex)。Flexible - 使用允许子组件在特定限制内调整自身尺寸,同时仍可扩展/收缩。使用
Flexible因子定义子组件之间的空间消耗比例。flex
- 使用
- **宽度约束:**防止组件在大屏幕上占用所有水平空间。将或
GridView等组件包裹在ListView或ConstrainedBox中,并在Container中定义BoxConstraints。maxWidth - **懒加载渲染:**当渲染数量未知或数量庞大的列表项时,始终使用或
ListView.builder。GridView.builder
Device and Orientation Behaviors
设备与方向行为
Ensure the app behaves correctly across all device form factors and input methods.
- Do not lock screen orientation. Locking orientation causes severe layout issues on foldable devices, often resulting in letterboxing (the app centered with black borders). Android large format tiers require both portrait and landscape support.
- Fallback for Locked Orientation: If business requirements strictly mandate a locked orientation, use the to retrieve physical screen dimensions instead of
Display API.MediaQueryfails to receive the larger window size in compatibility modes.MediaQuery - Support Multiple Inputs: Implement support for basic mice, trackpads, and keyboard shortcuts. Ensure touch targets are appropriately sized and keyboard navigation is accessible.
确保应用在所有设备形态和输入方式下都能正常运行。
- **不要锁定屏幕方向。**锁定方向会在折叠屏设备上导致严重的布局问题,通常会出现黑边(应用居中显示,周围有黑色边框)。Android大尺寸设备要求同时支持竖屏和横屏。
- **锁定方向的 fallback 方案:**如果业务需求严格要求锁定方向,请使用获取物理屏幕尺寸,而非
Display API。在兼容模式下,MediaQuery无法获取更大的窗口尺寸。MediaQuery - **支持多种输入方式:**实现对基础鼠标、触控板和键盘快捷键的支持。确保触摸目标尺寸合适,且键盘导航可访问。
Workflow: Constructing an Adaptive Layout
工作流:构建自适应布局
Follow this workflow to implement a layout that adapts to the available .
BoxConstraintsTask Progress:
- Identify the target widget that requires adaptive behavior.
- Wrap the widget tree in a .
LayoutBuilder - Extract the from the builder callback.
constraints.maxWidth - Define an adaptive breakpoint (e.g., ).
largeScreenMinWidth = 600 - If : Return a large-screen layout (e.g., a
maxWidth > largeScreenMinWidthplacing a navigation sidebar and content area side-by-side).Row - If : Return a small-screen layout (e.g., a
maxWidth <= largeScreenMinWidthor standard navigation-style approach).Column - Run validator -> resize the application window -> review layout transitions -> fix overflow errors.
遵循此工作流实现可适配的布局。
BoxConstraints任务进度:
- 确定需要自适应行为的目标组件。
- 将组件树包裹在中。
LayoutBuilder - 从构建器回调中提取。
constraints.maxWidth - 定义自适应断点(例如)。
largeScreenMinWidth = 600 - **如果:**返回大屏幕布局(例如,使用
maxWidth > largeScreenMinWidth将导航侧边栏和内容区域并排显示)。Row - **如果:**返回小屏幕布局(例如,使用
maxWidth <= largeScreenMinWidth或标准导航样式)。Column - 运行验证器 -> 调整应用窗口大小 -> 检查布局过渡 -> 修复溢出错误。
Workflow: Optimizing for Large Screens
工作流:针对大屏幕优化
Follow this workflow to prevent UI elements from stretching unnaturally on large displays.
Task Progress:
- Identify full-width components (e.g., , text blocks, forms).
ListView - If optimizing a list: Convert to
ListView.builderusingGridView.builderto automatically adjust column counts based on window size.SliverGridDelegateWithMaxCrossAxisExtent - If optimizing a form or text block: Wrap the component in a .
ConstrainedBox - Apply to the
BoxConstraints(maxWidth: [optimal_width]).ConstrainedBox - Wrap the in a
ConstrainedBoxwidget to keep the constrained content centered on large screens.Center - Run validator -> test on desktop/tablet target -> review horizontal stretching -> adjust or grid extents.
maxWidth
遵循此工作流防止UI元素在大屏幕上过度拉伸。
任务进度:
- 识别全宽组件(例如、文本块、表单)。
ListView - **如果优化列表:**将转换为
ListView.builder,使用GridView.builder根据窗口大小自动调整列数。SliverGridDelegateWithMaxCrossAxisExtent - **如果优化表单或文本块:**将组件包裹在中。
ConstrainedBox - 为应用
ConstrainedBox。BoxConstraints(maxWidth: [optimal_width]) - 将包裹在
ConstrainedBox组件中,使受约束的内容在大屏幕上居中显示。Center - 运行验证器 -> 在桌面/平板目标设备上测试 -> 检查水平拉伸情况 -> 调整或网格范围。
maxWidth
Examples
示例
Adaptive Layout using LayoutBuilder
使用LayoutBuilder实现自适应布局
Demonstrates switching between a mobile and desktop layout based on available width.
dart
import 'package:flutter/material.dart';
const double largeScreenMinWidth = 600.0;
class AdaptiveLayout extends StatelessWidget {
const AdaptiveLayout({super.key});
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);
}
}演示根据可用宽度在移动设备和桌面布局之间切换。
dart
import 'package:flutter/material.dart';
const double largeScreenMinWidth = 600.0;
class AdaptiveLayout extends StatelessWidget {
const AdaptiveLayout({super.key});
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);
}
}Constraining Width on Large Screens
限制大屏幕上的宽度
Demonstrates preventing a widget from consuming all horizontal space.
dart
import 'package:flutter/material.dart';
class ConstrainedContent extends StatelessWidget {
const ConstrainedContent({super.key});
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: 800.0, // Maximum width for readability
),
child: ListView.builder(
itemCount: 50,
itemBuilder: (context, index) {
return ListTile(
title: Text('Item $index'),
);
},
),
),
),
);
}
}演示防止组件占用所有水平空间。
dart
import 'package:flutter/material.dart';
class ConstrainedContent extends StatelessWidget {
const ConstrainedContent({super.key});
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: 800.0, // Maximum width for readability
),
child: ListView.builder(
itemCount: 50,
itemBuilder: (context, index) {
return ListTile(
title: Text('Item $index'),
);
},
),
),
),
);
}
}