mcpField
F
flutter-build-responsive-layout

flutter-build-responsive-layout

github.com/dhruvanbhalara设计与 UI3058 次安装9 周前更新
代码仓库

关于 flutter-build-responsive-layout

为 Flutter 开发者提供构建自适应布局的指导,重点依据窗口约束调整界面,并覆盖大屏可读性、动态窗口和多种输入方式。

主要功能

  1. 01按窗口尺寸选择布局
  2. 02使用 LayoutBuilder 读取约束
  3. 03通过 Flex 组件分配空间
  4. 04限制大屏内容最大宽度
  5. 05为长列表启用惰性构建
  6. 06支持多种输入方式

适用场景

  1. 01为手机与大屏设计不同布局
  2. 02适配可调整大小或分屏窗口
  3. 03避免宽屏表单和文本过度拉伸

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/dhruvanbhalara/skills --skill flutter-build-responsive-layout

安装后在 Claude Code 中输入/flutter-build-responsive-layout确认已加载。

详细介绍

目录

空间测量指南

为确保布局能够随应用窗口灵活调整(这对于支持可调整大小窗口或多窗口多任务处理的平台尤为重要),请遵循以下原则来确定可用空间:

  • 使用 MediaQuery.sizeOf(context):不要访问完整的 MediaQuery.of(context)(这会因任何 MediaQuery 属性变化而触发重建),而应使用现代且高效的 MediaQuery.sizeOf(context) 获取应用窗口的尺寸。
  • 使用 LayoutBuilder:如果希望组件根据父组件分配给它的尺寸(而非全局屏幕尺寸)进行调整,请将布局决策放在 LayoutBuilder 中。使用 constraints.maxWidth 选择要渲染的合适组件子树。
  • 避免使用全局方向构建器:不要在组件树顶部附近依赖设备方向构建器(OrientationBuilder 或 MediaQuery.orientationOf)来切换主布局。设备方向并不总是能反映实际可用的布局空间,尤其是在多窗口或折叠设备多任务模式下。
  • 不要检查物理硬件类型:不要通过判断当前硬件是“手机”“平板”还是“桌面”来确定布局规则。布局必须适应窗口约束,而不是物理硬件类型,因为可调整大小的窗口、画中画和屏幕分屏都是现代平台的标准功能。

组件尺寸与约束

在 Flutter 中,布局通过协商流程构建:约束向下传递,尺寸向上传递,位置由父组件设置。应用以下规则,以优雅地管理组件尺寸:

  • 使用 Flex 分配空间:在 Row、Column 或 Flex 布局中使用 Expanded 和 Flexible,按比例在同级组件之间分配可用空间。
    • 使用 Expanded 强制子组件沿主轴占用所有剩余可用空间。
    • 使用 Flexible 允许子组件在特定限制内自行调整尺寸,并使用 flex 因子定义剩余空间的分配方式。
  • 限制最大宽度:避免内容在宽屏上无限延展。将列表视图、网格和表单区块放入 ConstrainedBox,或放入带有最大宽度限制的 Container,并使用 Center 组件将其居中。
  • 延迟渲染列表:显示包含大量项目或项目数量动态变化的列表或网格时,始终使用 ListView.builder 或 GridView.builder,确保屏幕外的组件按需构建,并保持较低的内存占用。

设备与方向行为

响应式应用能够适应动态变化的外形规格和多种输入方式:

  • 避免锁定屏幕方向:强烈不建议将应用锁定为单一方向。这会破坏用户在折叠设备和大屏设备上的体验,导致出现不协调的黑边。
  • 提供安全的回退方案:如果因严格的业务要求而无法避免锁定屏幕方向,请使用 Display API 获取物理屏幕尺寸,而不要使用 MediaQuery;在兼容模式或旧版缩放模式下,后者可能会返回不正确的尺寸。
  • 支持多种输入方式:确保所有交互式组件的最小触控目标尺寸为 48x48 逻辑像素。优化布局组件,使其能够响应鼠标悬停、触控板手势和键盘导航。

工作流:构建自适应布局

按照此清单构建并验证响应式布局:

  • 确定目标布局:选择需要自定义响应式行为的页面或部分。
  • 实现 LayoutBuilder:使用 LayoutBuilder widget 包裹子树。
  • 提取约束宽度:在构建器回调中读取 constraints.maxWidth。
  • 定义明确的断点:使用既定断点(例如,平板电脑/大屏布局采用 600 个逻辑像素)。
  • 定义子树:
    • 如果 maxWidth > 600:返回多窗格或分屏布局(例如,将导航侧边栏置于内容旁边)。
    • 如果 maxWidth <= 600:返回单列或针对移动设备优化的布局。
  • 检查并验证:运行应用,动态调整窗口大小或旋转模拟器,并确认转换过程中没有布局溢出或卡顿。

工作流:针对大屏幕进行优化

按照此清单优化大屏幕上的可读性:

  • 找出被拉伸的组件:查找横跨整个屏幕宽度的全文本块、输入表单或列表项。
  • 应用约束:使用 ConstrainedBox widget 包裹被拉伸的内容。
  • 设定最大宽度:设置易读的最大宽度(例如,BoxConstraints(maxWidth: 800.0))。
  • 将内容居中:使用 Center widget 包裹 ConstrainedBox,让宽屏显示器上的布局保持平衡。
  • 检查并验证:在桌面构建版本或平板模拟器上测试布局,确保可读性依然出色,且内容不会被拉伸。

示例

使用断点实现自适应布局

此示例展示如何根据可用空间,在针对移动设备优化的布局与适用于大屏幕的分栏布局之间切换。

import 'package:flutter/material.dart';

class AdaptiveHomeLayout extends StatelessWidget {
  const AdaptiveHomeLayout({super.key});

  static const double tabletBreakpoint = 600.0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: LayoutBuilder(
        builder: (context, constraints) {
          if (constraints.maxWidth > tabletBreakpoint) {
            return _buildTabletLayout();
          }
          return _buildMobileLayout();
        },
      ),
    );
  }

  Widget _buildTabletLayout() {
    return Row(
      children: [
        const SizedBox(
          width: 240,
          child: NavigationSidebar(),
        ),
        const VerticalDivider(width: 1),
        Expanded(
          child: const MainContentArea(),
        ),
      ],
    );
  }

  Widget _buildMobileLayout() {
    return const MainContentArea();
  }
}

class NavigationSidebar extends StatelessWidget {
  const NavigationSidebar({super.key});

  @override
  Widget build(BuildContext context) {
    return ListView(
      padding: const EdgeInsets.symmetric(vertical: 16),
      children: const [
        ListTile(
          leading: Icon(Icons.dashboard),
          title: Text('Dashboard'),
        ),
        ListTile(
          leading: Icon(Icons.settings),
          title: Text('Settings'),
        ),
      ],
    );
  }
}

class MainContentArea extends StatelessWidget {
  const MainContentArea({super.key});

  @override
  Widget build(BuildContext context) {
    return const Center(
      child: Text('Main Content Area'),
    );
  }
}
限制宽屏上的宽度

此示例可防止个人资料或设置表单在宽屏桌面上被不自然地拉伸。

import 'package:flutter/material.dart';

class ConstrainedProfileForm extends StatelessWidget {
  const ConstrainedProfileForm({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Edit Profile'),
      ),
      body: Center(
        child: ConstrainedBox(
          constraints: const BoxConstraints(
            maxWidth: 600.0,
          ),
          child: ListView(
            padding: const EdgeInsets.all(24.0),
            children: [
              const TextFormField(
                decoration: InputDecoration(
                  labelText: 'Full Name',
                  border: OutlineInputBorder(),
                ),
              ),
              const SizedBox(height: 16),
              const TextFormField(
                decoration: InputDecoration(
                  labelText: 'Email Address',
                  border: OutlineInputBorder(),
                ),
              ),
              const SizedBox(height: 24),
              ElevatedButton(
                onPressed: () {},
                child: const Text('Save Changes'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

评论

支持 Markdown