<返回目录     Powered by claud/xia兄

第4课:布局组件深度解析

Container、Row、Column、Stack、Flex、Expanded

📋 学习前提

🎯 课程目标

通过本课程,你将能够:

🚀 Flutter布局系统概述

Flutter的布局系统基于约束(Constraints)尺寸(Size)的概念:

布局流程的三个阶段

  1. 约束传递(Constraints Pass):父Widget向子Widget传递约束条件
  2. 尺寸确定(Size Determination):子Widget根据约束确定自己的尺寸
  3. 位置确定(Position Determination):父Widget根据子Widget的尺寸确定位置
💡 重要概念:约束系统

Flutter使用约束系统来布局Widget。每个Widget都会收到来自父Widget的约束(最小/最大宽度和高度),然后在这个约束范围内确定自己的尺寸。

常用布局组件分类

📦 Container(容器组件)

Container的基本用法

Container是最常用的布局组件,可以设置尺寸、边距、填充、装饰等:

import 'package:flutter/material.dart'; class ContainerExample extends StatelessWidget { @override Widget build(BuildContext context) { return Container( // 尺寸设置 width: 200, height: 100, // 边距和填充 margin: const EdgeInsets.all(20), // 外边距 padding: const EdgeInsets.all(16), // 内边距 // 装饰效果 decoration: BoxDecoration( color: Colors.blue, // 背景颜色 borderRadius: BorderRadius.circular(12), // 圆角 border: Border.all( color: Colors.black, width: 2, ), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.3), blurRadius: 8, offset: const Offset(2, 4), ), ], ), // 子Widget child: const Text( 'Hello Container', style: TextStyle(color: Colors.white, fontSize: 18), ), ); } }

Container的约束行为

Container在没有子Widget和有子Widget时的行为不同:

// 没有子Widget:尝试尽可能大(在约束范围内) Container( color: Colors.red, width: double.infinity, // 无限宽度 height: 100, ) // 有子Widget:包裹子Widget的大小 Container( color: Colors.blue, child: Text('这个Container会包裹文本大小'), ) // 同时指定尺寸和子Widget:使用指定尺寸 Container( width: 200, height: 100, child: Text('这个Container使用固定尺寸'), )
💡 最佳实践:合理使用Container

Container功能强大但相对较重,对于简单的装饰效果,可以考虑使用更轻量的组件如Padding、DecoratedBox等。

➡️ Row(水平布局)

Row的基本用法

Row用于在水平方向上排列子Widget:

Row( // 主轴对齐方式 mainAxisAlignment: MainAxisAlignment.spaceBetween, // 交叉轴对齐方式 crossAxisAlignment: CrossAxisAlignment.center, // 主轴尺寸行为 mainAxisSize: MainAxisSize.max, children: [ Container( width: 50, height: 50, color: Colors.red, child: const Center(child: Text('1')), ), Container( width: 50, height: 70, color: Colors.green, child: const Center(child: Text('2')), ), Container( width: 50, height: 30, color: Colors.blue, child: const Center(child: Text('3')), ), ], )

主轴对齐方式(mainAxisAlignment)

start
center
end

交叉轴对齐方式(crossAxisAlignment)

⬇️ Column(垂直布局)

Column的基本用法

Column用于在垂直方向上排列子Widget:

Column( // 主轴对齐方式(垂直方向) mainAxisAlignment: MainAxisAlignment.spaceEvenly, // 交叉轴对齐方式(水平方向) crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Container( height: 60, color: Colors.red, child: const Center(child: Text('标题')), ), Container( height: 100, color: Colors.green, child: const Center(child: Text('内容')), ), Container( height: 40, color: Colors.blue, child: const Center(child: Text('底部')), ), ], )

Column的常见布局模式

// 表单布局 Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('用户名:', style: TextStyle(fontWeight: FontWeight.bold)), TextField(decoration: const InputDecoration(hintText: '请输入用户名')), const SizedBox(height: 16), const Text('密码:', style: TextStyle(fontWeight: FontWeight.bold)), TextField(decoration: const InputDecoration(hintText: '请输入密码'), obscureText: true), const SizedBox(height: 24), ElevatedButton(onPressed: () {}, child: const Text('登录')), ], ) // 卡片列表布局 Column( children: [ Card( child: ListTile( leading: const Icon(Icons.person), title: const Text('张三'), subtitle: const Text('前端工程师'), trailing: const Icon(Icons.arrow_forward), onTap: () {}, ), ), Card( child: ListTile( leading: const Icon(Icons.person), title: const Text('李四'), subtitle: const Text('后端工程师'), trailing: const Icon(Icons.arrow_forward), onTap: () {}, ), ), ], )

📐 Expanded和Flexible

Expanded(扩展组件)

Expanded让子Widget填充Row或Column的剩余空间:

Row( children: [ Container( width: 50, height: 50, color: Colors.red, ), Expanded( flex: 2, // 权重为2 child: Container( height: 50, color: Colors.green, child: const Center(child: Text('占剩余空间的2/3')), ), ), Expanded( flex: 1, // 权重为1 child: Container( height: 50, color: Colors.blue, child: const Center(child: Text('占剩余空间的1/3')), ), ), ], )

Flexible(灵活组件)

Flexible比Expanded更灵活,可以控制是否填满剩余空间:

Column( children: [ Flexible( flex: 1, fit: FlexFit.tight, // 填满剩余空间(同Expanded) child: Container(color: Colors.red, child: const Center(child: Text('填满'))), ), Flexible( flex: 1, fit: FlexFit.loose, // 不强制填满,按内容大小 child: Container( color: Colors.green, child: const Center(child: Text('不填满')), ), ), Container( height: 50, color: Colors.blue, child: const Center(child: Text('固定高度')), ), ], )
💡 重要概念:flex权重

flex属性决定子Widget在剩余空间中的分配比例。例如,flex为2:1的两个Expanded,第一个将获得2/3的空间,第二个获得1/3的空间。

🔄 Stack(层叠布局)

Stack的基本用法

Stack用于将子Widget层叠在一起:

Stack( // 尺寸行为 fit: StackFit.expand, // 填满父容器 // 对齐方式 alignment: Alignment.center, children: [ // 背景层 Container( color: Colors.grey[300], ), // 内容层 const Positioned( top: 20, left: 20, child: Text('左上角文字'), ), // 浮动按钮层 Positioned( bottom: 20, right: 20, child: FloatingActionButton( onPressed: () {}, child: const Icon(Icons.add), ), ), // 居中层 const Center( child: Text('居中文字', style: TextStyle(fontSize: 24)), ), ], )

Positioned组件

Positioned用于在Stack中精确定位子Widget:

Stack( children: [ // 使用具体位置 Positioned( top: 10, left: 10, child: Container(color: Colors.red, width: 50, height: 50), ), // 使用百分比位置 Positioned.fill( top: 20, // 距顶部20% left: 20, // 距左侧20% child: Container(color: Colors.green), ), // 使用fromRect Positioned.fromRect( rect: const Rect.fromLTWH(100, 100, 80, 60), child: Container(color: Colors.blue), ), ], )
📝 示例:头像徽章效果
Stack( children: [ // 头像 Container( width: 80, height: 80, decoration: const BoxDecoration( shape: BoxShape.circle, image: DecorationImage( image: NetworkImage('https://example.com/avatar.jpg'), fit: BoxFit.cover, ), ), ), // 在线状态徽章 Positioned( bottom: 0, right: 0, child: Container( width: 20, height: 20, decoration: const BoxDecoration( color: Colors.green, shape: BoxShape.circle, border: Border.fromBorderSide( BorderSide(color: Colors.white, width: 2), ), ), ), ), ], )

📚 实践练习

练习1:创建个人资料卡片

使用Column和Row创建个人资料卡片:

  1. 顶部显示头像和基本信息(使用Row)
  2. 中间显示个人简介(使用Column)
  3. 底部显示技能标签(使用Wrap)
  4. 添加阴影和圆角效果

练习2:实现商品列表项

使用Flex布局实现商品列表项:

  1. 左侧显示商品图片
  2. 中间显示商品信息和价格(使用Expanded)
  3. 右侧显示购买按钮
  4. 实现响应式布局,适配不同屏幕尺寸

练习3:创建浮动导航栏

使用Stack实现浮动导航栏:

  1. 底部显示主内容区域
  2. 在右下角添加浮动按钮
  3. 实现按钮点击展开菜单效果
  4. 添加动画过渡效果

🔍 常见问题解答

Q: Row和Column中的子Widget溢出怎么办?

A: 使用Expanded或Flexible让子Widget自适应空间,或者使用SingleChildScrollView包装。

Q: 如何实现等分布局?

A: 使用MainAxisAlignment.spaceEvenly或为每个子Widget包裹Expanded并设置相同的flex值。

Q: Stack中的子Widget如何居中?

A: 使用Alignment.center设置Stack的alignment属性,或者使用Center组件包裹子Widget。

Q: 如何实现响应式布局?

A: 使用MediaQuery获取屏幕尺寸,结合LayoutBuilder根据约束条件动态调整布局。

📖 总结

在本课程中,我们深入学习了Flutter的核心布局组件:

布局是Flutter UI开发的基础,熟练掌握这些组件将帮助你构建出美观、灵活的界面。