Flutter 布局入门

Flutter 布局的核心思想

约束向下传递,尺寸向上传递

这是 Flutter 布局最精髓的部分,可以类比为父母和孩子商量如何占用房间空间:

1、父级传递约束

父组件会向子组件传递一个约束。这个约束规定了子组件可以有多大,通常是一个最小和最大的宽度/高度范围。

例如:BoxConstraints(minWidth: 50, maxWidth: 200, minHeight: 10, maxHeight: 100)

意思是:"孩子,你的宽度必须在 50 到 200 之间,高度必须在 10 到 100 之间,你自己看着办。

2、子级决定尺寸

子组件在父级给的约束范围内,决定自己的尺寸

  • 子组件必须遵守这个约束。它不能超出最大范围,也不能小于最小范围。

  • 子组件决定好尺寸后,将这个尺寸报告给父级。

3、父级进行定位

父组件拿到子组件的尺寸后,在父组件自己的空间内将子组件定位

整个过程是递归进行的:从最外层组件开始,逐级向下传递约束,子组件决定尺寸后再逐级向上报告,最终完成整个 UI 的布局。

常见布局 Widget 分类表

类型 代表 Widget 适用场景 核心特性
单子布局 Container 包裹单个子组件,添加装饰或边距 可设置宽高、边距、颜色、圆角等装饰属性
Padding 为子组件添加内边距 简单的内边距控制
Center 子组件居中显示 水平和垂直方向都居中
Align 自定义对齐方式 支持多种对齐方式(topLeft、center等)
SizedBox 固定尺寸盒子 强制子组件具有指定尺寸
多子线性布局 Row 水平排列多个组件 主轴为水平方向,支持多种对齐方式
Column 垂直排列多个组件 主轴为垂直方向,支持多种对齐方式
Flex 弹性布局(Row/Column的父类) 可自定义主轴方向
层叠布局 Stack 组件叠加显示 子组件可以重叠显示
Positioned 在Stack中精确定位 可设置top、bottom、left、right定位
IndexedStack 显示Stack中指定索引的子组件 每次只显示一个子组件
滚动布局 ListView 线性滚动列表 支持垂直/水平滚动,适合长列表
GridView 网格布局滚动 以网格形式排列内容
SingleChildScrollView 单子组件滚动 包裹单个可滚动子组件
CustomScrollView 自定义滚动效果 可组合多个Sliver组件
弹性子项 Expanded 在Row/Column中扩展填充 占据剩余空间,可设置flex权重
Flexible 灵活空间分配 可设置fit属性控制空间占用方式
其他布局 AspectRatio 保持宽高比 强制子组件保持指定宽高比
ConstrainedBox 附加约束条件 为子组件添加额外的尺寸约束
FittedBox 缩放适配 根据父容器缩放子组件

快速选择指南

根据场景选择布局:

  • 简单排列:Row / Column

  • 重叠显示:Stack + Positioned

  • 列表展示:ListView

  • 网格展示:GridView

  • 装饰包装:Container

  • 空间分配:Expanded / Flexible

  • 滚动内容:SingleChildScrollView

常用组合模式:

Dart 复制代码
// 典型页面结构
Scaffold(
  body: Column(
    children: [
      Container(), // 头部
      Expanded(    // 内容区域
        child: ListView(), // 可滚动内容
      ),
      Stack(       // 底部浮动按钮
        children: [
          Container(),
          Positioned(),
        ],
      ),
    ],
  ),
)

布局技巧

技巧 1:用 ExpandedFlexible 实现弹性空间分配

当需要让子组件按比例占据剩余空间时,使用 ExpandedFlexible

Dart 复制代码
Row(
  children: [
    // 占据 1/3 空间
    Expanded(
      flex: 1,
      child: Container(color: Colors.red),
    ),
    // 占据 2/3 空间
    Expanded(
      flex: 2,
      child: Container(color: Colors.blue),
    ),
  ],
)
  • Expanded:强制子组件填满剩余空间。

  • Flexible:允许子组件不填满剩余空间(通过 fit 属性控制)。

技巧 2:用 SizedBox 精确控制尺寸

避免直接设置 width/height,优先使用 SizedBox 或约束:

Dart 复制代码
SizedBox(
  width: 100,
  height: 50,
  child: ElevatedButton(onPressed: () {}, child: Text('按钮')),
)

SizedBox 是一个用于明确指定其宽度和高度的小部件(Widget)。它通常用于在布局中提供固定大小的空间,或者在需要明确指定尺寸时使用。

技巧 3:处理内容溢出(Overflow)

当内容超出父容器时,常见的解决方案:

Dart 复制代码
// 方案 1:使用 SingleChildScrollView 包裹
SingleChildScrollView(
  child: ... // 长内容
)
 
// 方案 2:使用溢出提示(仅 Debug 模式可见)
OverflowBox(
  child: ...,
)
 
// 方案 3:裁剪溢出内容
ClipRect(
  child: ...,
)
技巧 4:用 Stack 实现层叠布局

通过 Positioned 控制子组件的位置:

Dart 复制代码
Stack(
  children: [
    Container(color: Colors.grey), // 底层背景
    Positioned(
      top: 10,
      right: 10,
      child: Icon(Icons.star), // 右上角图标
    Positioned.fill( // 填充剩余区域
      child: Center(
        child: Text('居中文字'),
      ),
    ),
  ],
)
技巧 5:用 LayoutBuilder 响应式布局

根据父容器的尺寸动态调整布局:

Dart 复制代码
LayoutBuilder(
  builder: (context, constraints) {
    if (constraints.maxWidth > 600) {
      return _buildWideLayout(); // 宽屏布局
    } else {
      return _buildNarrowLayout(); // 窄屏布局
    }
  },
)
技巧 6:嵌套布局的性能优化
  • 避免过度嵌套 :使用 Column + Row 组合替代多层 Container

  • 使用 const 构造函数:减少 Widget 重建开销。

常见错误与解决方案

错误 1:RenderBox overflowed by XX pixels

原因:内容超出容器边界。

解决:使用 SingleChildScrollView 或调整布局约束。

错误 2:Incorrect use of ParentDataWidget

原因:未在 Row/Column 的子组件中正确使用 Expanded。

解决:确保直接子组件是 Expanded 或 Flexible。

错误 3:Null check operator used on a null value

原因:在未初始化的 Text 组件中使用 ! 操作符。

解决:提供默认值或检查数据源

总结

Flutter 布局的核心在于理解 约束传递机制 和 Widget 组合思想。通过合理使用 Row、Column、Expanded 等基础组件,结合性能优化技巧,可以高效实现复杂界面。

记住以下原则:

先拆解:将复杂 UI 分解为多个简单布局。

善用嵌套:但避免超过 4 层嵌套。

及时调试:利用 Flutter 工具快速定位问题。

下一篇

现在布局弄清楚,那么布局里面填充的内容呢?

所有界面元素都是由 Widget 组成。Flutter 中的 Widget 分为 StatelessWidget(无状态组件)StatefulWidget(有状态组件),它们在开发过程中扮演着不同的角色。

Flutter 组件:StatelessWidget vs StatefulWidget

相关推荐
Q688238861 小时前
三菱Q系列PLC大型自动化生产线程序案例分享
flutter
天天开发20 小时前
Flutter每日库: image_picker选取相册图片视频
flutter
消失的旧时光-19431 天前
Flutter 组件:StatelessWidget vs StatefulWidget
flutter
天意__1 天前
Flutter 聊天界面使用ListView的reverse:true,导致条目太少的时候会从下往上显示,导致顶部大片空白
flutter
汤面不加鱼丸1 天前
flutter实践:混合app在部分android旧机型上显示异常
android·flutter
火柴就是我1 天前
flutter 为什么大家说不能在initState 方法中调用dependOnInheritedWidgetOfExactType
flutter
程序员老刘2 天前
4:2:1!老刘的三季度项目报告
flutter·harmonyos·客户端
达达尼昂2 天前
🎯 Flutter 拖拽选择组件:flutter_drag_selector —— 像选文件一样选择列表项
前端·flutter