flutter-build-responsive-layout

flutter-build-responsive-layout

热门

使用 `LayoutBuilder`、`MediaQuery` 或 `Expanded/Flexible` 创建适应不同屏幕尺寸的布局。当您需要 UI 在移动端和平板/桌面端形态下都看起来良好时使用。

2525Star
152Fork
更新于 2026/6/18
SKILL.md
只读
名称
flutter-build-responsive-layout
描述

使用 `LayoutBuilder`、`MediaQuery` 或 `Expanded/Flexible` 创建适应不同屏幕尺寸的布局。当您需要 UI 在移动端和平板/桌面端形态下都看起来良好时使用。

实现自适应布局

目录

空间测量指南

准确确定可用空间,确保布局适应应用窗口,而非仅物理设备。

  • 使用 MediaQuery.sizeOf(context) 获取整个应用窗口的大小。
  • 使用 LayoutBuilder 根据父组件分配的空间做出布局决策。评估 constraints.maxWidth 以确定返回合适的组件树。
  • 不要在组件树顶层使用 MediaQuery.orientationOfOrientationBuilder 来切换布局。设备方向不能准确反映应用窗口的可用空间。
  • 不要检查硬件类型(例如“手机”与“平板”)。Flutter 应用运行在可调整大小的窗口、多窗口模式和画中画模式下。所有布局决策应严格基于可用窗口空间。

组件尺寸与约束

理解并应用 Flutter 的核心布局规则:约束向下传递。尺寸向上传递。父组件设置位置。

  • 分配空间:RowColumnFlex 组件中使用 ExpandedFlexible
    • 使用 Expanded 强制子组件填满所有剩余可用空间(等同于 flex 因子为 1.0 且 fit: FlexFit.tightFlexible)。
    • 使用 Flexible 允许子组件在扩展/收缩的同时,将自身大小限制在特定范围内。使用 flex 因子定义兄弟组件间的空间消耗比例。
  • 限制宽度: 防止组件在大屏幕上占用所有水平空间。将 GridViewListView 等组件包裹在 ConstrainedBoxContainer 中,并在 BoxConstraints 中定义 maxWidth
  • 懒加载: 当渲染未知数量或大量列表项时,始终使用 ListView.builderGridView.builder

设备与方向行为

确保应用在所有设备形态和输入方式下行为正确。

  • 不要锁定屏幕方向。 锁定方向会在可折叠设备上导致严重的布局问题,通常会出现黑边(应用居中并带有黑色边框)。Android 大屏层级要求同时支持竖屏和横屏。
  • 锁定方向的回退方案: 如果业务需求强制要求锁定方向,请使用 Display API 获取物理屏幕尺寸,而不是 MediaQueryMediaQuery 在兼容模式下无法获取更大的窗口尺寸。
  • 支持多种输入: 实现对基本鼠标、触控板和键盘快捷键的支持。确保触摸目标尺寸合适,键盘导航可访问。

工作流:构建自适应布局

按照以下工作流实现适应可用 BoxConstraints 的布局。

任务进度:

  • [ ] 确定需要自适应行为的目标组件。
  • [ ] 将组件树包裹在 LayoutBuilder 中。
  • [ ] 从 builder 回调中提取 constraints.maxWidth
  • [ ] 定义自适应断点(例如 largeScreenMinWidth = 600)。
  • [ ] 如果 maxWidth > largeScreenMinWidth 返回大屏布局(例如,使用 Row 将导航侧边栏和内容区域并排放置)。
  • [ ] 如果 maxWidth <= largeScreenMinWidth 返回小屏布局(例如,使用 Column 或标准导航方式)。
  • [ ] 运行验证器 -> 调整应用窗口大小 -> 检查布局过渡 -> 修复溢出错误。

工作流:针对大屏优化

按照以下工作流防止 UI 元素在大屏幕上过度拉伸。

任务进度:

  • [ ] 识别全宽组件(例如 ListView、文本块、表单)。
  • [ ] 如果优化列表:ListView.builder 转换为使用 SliverGridDelegateWithMaxCrossAxisExtentGridView.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'),
              );
            },
          ),
        ),
      ),
    );
  }
}