flutter-build-responsive-layout

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Implementing 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
    MediaQuery.sizeOf(context)
    to get the size of the entire app window.
  • Use
    LayoutBuilder
    to make layout decisions based on the parent widget's allocated space. Evaluate
    constraints.maxWidth
    to determine the appropriate widget tree to return.
  • Do not use
    MediaQuery.orientationOf
    or
    OrientationBuilder
    near the top of the widget tree to switch layouts. Device orientation does not accurately reflect the available app window space.
  • 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
    Expanded
    and
    Flexible
    within
    Row
    ,
    Column
    , or
    Flex
    widgets.
    • Use
      Expanded
      to force a child to fill all remaining available space (equivalent to
      Flexible
      with
      fit: FlexFit.tight
      and a
      flex
      factor of 1.0).
    • Use
      Flexible
      to allow a child to size itself up to a specific limit while still expanding/contracting. Use the
      flex
      factor to define the ratio of space consumption among siblings.
  • Constrain Width: Prevent widgets from consuming all horizontal space on large screens. Wrap widgets like
    GridView
    or
    ListView
    in a
    ConstrainedBox
    or
    Container
    and define a
    maxWidth
    in the
    BoxConstraints
    .
  • Lazy Rendering: Always use
    ListView.builder
    or
    GridView.builder
    when rendering lists with an unknown or large number of items.
理解并应用Flutter的核心布局规则:约束向下传递,尺寸向上反馈,父组件设置位置。
  • **空间分配:**在
    Row
    Column
    Flex
    组件中使用
    Expanded
    Flexible
    • 使用
      Expanded
      强制子组件填充所有剩余可用空间(等同于
      fit: FlexFit.tight
      flex
      因子为1.0的
      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
    Display API
    to retrieve physical screen dimensions instead of
    MediaQuery
    .
    MediaQuery
    fails to receive the larger window size in compatibility modes.
  • 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
BoxConstraints
.
Task Progress:
  • Identify the target widget that requires adaptive behavior.
  • Wrap the widget tree in a
    LayoutBuilder
    .
  • Extract the
    constraints.maxWidth
    from the builder callback.
  • Define an adaptive breakpoint (e.g.,
    largeScreenMinWidth = 600
    ).
  • If
    maxWidth > largeScreenMinWidth
    :
    Return a large-screen layout (e.g., a
    Row
    placing a navigation sidebar and content area side-by-side).
  • If
    maxWidth <= largeScreenMinWidth
    :
    Return a small-screen layout (e.g., a
    Column
    or standard navigation-style approach).
  • 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.,
    ListView
    , text blocks, forms).
  • If optimizing a list: Convert
    ListView.builder
    to
    GridView.builder
    using
    SliverGridDelegateWithMaxCrossAxisExtent
    to automatically adjust column counts based on window size.
  • If optimizing a form or text block: Wrap the component in a
    ConstrainedBox
    .
  • Apply
    BoxConstraints(maxWidth: [optimal_width])
    to the
    ConstrainedBox
    .
  • Wrap the
    ConstrainedBox
    in a
    Center
    widget to keep the constrained content centered on large screens.
  • Run validator -> test on desktop/tablet target -> review horizontal stretching -> adjust
    maxWidth
    or grid extents.
遵循此工作流防止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'),
              );
            },
          ),
        ),
      ),
    );
  }
}