flutter | 布局组件全解析(含实战代码)
Flutter布局系统基于约束传递机制,父组件设定约束范围,子组件决定具体尺寸。常用布局组件包括:单子组件(Container、Center等)、多子线性布局(Row/Column)、弹性布局(Flex)、层叠布局(Stack)、流式布局(Wrap)以及可滚动组件(ListView/CustomScrollView)。其中,Flex通过Expanded实现比例分配空间,CustomScrollVi
·
一、Flutter 布局的核心哲学:约束传递(Constraints)
Flutter 的布局过程遵循一个简单但强大的规则:
“父组件给子组件施加约束(Constraints),子组件在约束范围内决定自己的尺寸。”
这个过程分为两步:
- 向下传递约束(Parent → Child)
- 向上传递尺寸(Child → Parent)
约束(Constraints)是什么?
- 通常是一个
BoxConstraints对象 - 包含四个属性:
minWidth,maxWidth,minHeight,maxHeight - 子组件必须在这个范围内选择自己的宽高
举个例子:
Container(
color: Colors.red,
child: Container(
color: Colors.blue,
width: 300,
height: 200,
),
)
- 外层
Container没有明确尺寸 → 它会尽可能大(填满父级,如屏幕) - 内层
Container被允许在[0, ∞] × [0, ∞]范围内选择尺寸 → 它选了300×200
但如果把内层放在 Center 里:
Center(
child: Container(
color: Colors.blue,
// width/height 未指定!
),
)
Center给子组件的约束是:minWidth=0, maxWidth=∞(宽松约束)- 但
Container没有内容、没有尺寸 → 它会选择 最小尺寸:0×0 → 看不到!
子组件的尺寸不仅取决于自己,更取决于父组件给的“自由度”。
二、常用布局组件分类详解
单子布局(Single-child Layout)
| 组件 | 作用 | 特点 |
|---|---|---|
Container |
万能容器 | 可设置 padding/margin/color/alignment/constraints |
Padding |
内边距 | 比 Container 更轻量 |
Center |
居中对齐 | 默认填满父级,子居中 |
Align |
自定义对齐 | 支持 Alignment.topLeft 等 |
SizedBox |
固定尺寸 | width: 100 或 height: 50,常用于间隔 |
ConstrainedBox |
施加约束 | 直接使用 BoxConstraints |
实战技巧:
- 用
SizedBox(width: 16)替代Container(width: 16)更高效 Container是Padding+Align+DecoratedBox+ConstrainedBox的组合体
多子线性布局(Multi-child Linear)
| 组件 | 方向 | 是否可滚动 |
|---|---|---|
Row |
水平 | 不可滚动 |
Column |
垂直 | 不可滚动 |
ListView |
垂直(默认) | 可滚动 |
GridView |
网格 | 可滚动 |
常见错误:Expanded 与 Flexible
Row(
children: [
Text('Left'),
Expanded(child: Text('Fill remaining space')), // 正确
Text('Right'),
],
)
Expanded=Flexible(flex: 1, fit: FlexFit.tight)- 必须放在
Row/Column/Flex内部 - 如果子项总和超过可用空间 → 溢出错误(Yellow/Black Stripe)
解决溢出:
- 使用
SingleChildScrollView - 限制子项最大宽度:
ConstrainedBox(maxWidth: ...) - 使用
Text(overflow: TextOverflow.ellipsis)
弹性布局(Flex)
实现了一个简单的垂直方向弹性布局(Flex),将屏幕分为三部分,按比例分配高度。
import 'package:flutter/material.dart';
void main() {
runApp(MainPage());
}
class MainPage extends StatelessWidget{
const MainPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context){
return MaterialApp(
home:Scaffold(
body:Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
color: Colors.grey,
),
child:Flex(direction:Axis.vertical,
children:[
Expanded(
flex:2,
child:Container(
width:100,
color: Colors.blue,
),
),
Expanded(
flex:1,
child:Container(
width:100,
color: Colors.red,
),
),
Expanded(
flex:1,
child:Container(
width:100,
color: Colors.green,
),
),
]
)
)
)
);
}
}
实现效果:

层叠布局(Stack)
用于绝对定位、重叠元素(如浮层、徽标、自定义控件):
import 'package:flutter/material.dart';
void main() {
runApp(MainPage());
}
class MainPage extends StatelessWidget{
const MainPage({Key? key}) : super(key: key);
List<Widget> getList(){
return List.generate(10,(index){
return Container(
width:100,
height:100,
color: Colors.primaries[index],
);
});
}
@override
Widget build(BuildContext context){
return MaterialApp(
home:Scaffold(
body:Container(
width:double.infinity,
height:double.infinity,
color: Colors.grey,
child:
Stack(
alignment:Alignment.center,
children:[
Container(
width:300,
height:300,
color: Colors.blue,
),
Container(
width:200,
height:200,
color: Colors.red,
),
Container(
width:100,
height:100,
color: Colors.green,
),
Container(
width:50,
height:50,
color: Colors.yellow,
),
Positioned(
bottom:100,
right:100,
child:Container(
width:50,
height:50,
color: Colors.purple,
),
)
]
)
)
)
);
}
}
实现效果:

流式布局(Wrap)
实现了一个自动换行的色块网格,结合了静态定义的容器和动态生成的列表
import 'package:flutter/material.dart';
void main() {
runApp(MainPage());
}
class MainPage extends StatelessWidget{
const MainPage({Key? key}) : super(key: key);
List<Widget> getList(){
return List.generate(10,(index){
return Container(
width:100,
height:100,
color: Colors.primaries[index],
);
});
}
@override
Widget build(BuildContext context){
return MaterialApp(
home:Scaffold(
body:Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
color: Colors.grey,
),
child:Wrap(
direction:Axis.horizontal,
alignment:WrapAlignment.center,
spacing:10,
runSpacing:10,
children:[
Container(
width:100,
height:100,
color: Colors.blue,
),
Container(
width:100,
height:100,
color: Colors.red,
),
Container(
width:100,
height:100,
color: Colors.green,
),
...getList()
]
)
)
)
);
}
}
实现效果:


SingleChildScrollView组件案例
实现了一个带滚动功能的长列表页面,并提供了两个悬浮按钮:
- 顶部“向下箭头”:点击后平滑滚动到列表底部
- 底部“向上箭头”:点击后平滑滚动回顶部
整体使用 Stack 布局将可滚动内容与悬浮按钮叠加显示
import 'package:flutter/material.dart';
void main() {
runApp(MainPage());
}
class MainPage extends StatefulWidget {
MainPage({Key? key}) : super(key: key);
@override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
final ScrollController _controller = ScrollController();//滚动控制器
@override
Widget build(BuildContext context) {
return MaterialApp(
home:Scaffold(appBar:AppBar(title:Text("滚动列表"),) ,
body:Stack(
children: [
SingleChildScrollView(
controller: _controller,
padding: EdgeInsets.all(20),
child: Column(
children:List.generate(100, (index){
return Container(
margin: EdgeInsets.all(10),
width: double.infinity,
color: Colors.pink,
height: 100,
child:Text("我是第${index+1}个容器",style: TextStyle(color: Colors.white,fontSize: 20,)),
alignment: Alignment.center,
);
}),
),
),
Positioned(
right: 20,
top: 20,
child: GestureDetector(
onTap: (){
_controller.animateTo(_controller.position.maxScrollExtent, duration: Duration(seconds: 1), curve: Curves.ease);
print("点击了向下箭头");
},
child:Container(
decoration: BoxDecoration(
color: Colors.grey,
borderRadius: BorderRadius.circular(40),
),
padding: EdgeInsets.all(10),
child: Icon(Icons.arrow_circle_down,color: Colors.white,),
),
)
),
Positioned(
right: 20,
bottom: 20,
child: GestureDetector(
onTap: (){
_controller.animateTo(0, duration: Duration(seconds: 1), curve: Curves.ease);
print("点击了向上箭头");
},
child:Container(
decoration: BoxDecoration(
color: Colors.grey,
borderRadius: BorderRadius.circular(40),
),
padding: EdgeInsets.all(10),
child: Icon(Icons.arrow_circle_up,color: Colors.white,),
)
)
)
]
)));
}
}
实现效果:

CustomScrollView吸顶案例
使用 CustomScrollView + Sliver 系列组件 构建了一个复杂的、具有 吸顶效果(Sticky Header) 的滚动页面,包含轮播图、分类导航栏、商品网格和列表项
import 'package:flutter/material.dart';
void main() {
runApp(MainPage());
}
class MainPage extends StatefulWidget {
MainPage({Key? key}) : super(key: key);
@override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
final ScrollController _controller = ScrollController();//滚动控制器
@override
Widget build(BuildContext context) {
return MaterialApp(
home:Scaffold(appBar:AppBar(title:Text("滚动列表"),) ,
body:CustomScrollView(
slivers: [
SliverToBoxAdapter(
child: Container(
height: 100,
color: Colors.blue,
child:Text("轮播图",
style:TextStyle(
fontSize: 20,
color: Colors.white,
)),
alignment: Alignment.center,
),
),
SliverToBoxAdapter(
child:SizedBox(
height: 20,
)
),
SliverPersistentHeader(delegate: _StickyCategory(), pinned: true,),
SliverToBoxAdapter(
child:SizedBox(
height: 20,
)
),
SliverGrid.count(
crossAxisCount: 2,
mainAxisSpacing: 10,
crossAxisSpacing: 10,
children:List.generate(100,(index){
return Container(alignment: Alignment.center,child: Text("第${index+1}个商品",style: TextStyle(fontSize: 20,color: Colors.white,),),color: Colors.blue,);
}),
),
SliverList.separated(
itemCount: 100,
separatorBuilder: (BuildContext context, int index) => SizedBox(height: 10,),
itemBuilder: (BuildContext context, int index) {
return Container(
height: 100,
color: Colors.blue,
child: Text('Item $index'),
);
},
),
],
)
));
}
}
class _StickyCategory extends SliverPersistentHeaderDelegate {
@override
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
return Container(
child:ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: 10,
itemBuilder: (BuildContext context,int index){
return Container(
width: 100,
color: Colors.blue,
alignment: Alignment.center,
margin: EdgeInsets.symmetric(horizontal: 10),
child:Text("第${index+1}个分类",
style:TextStyle(
color: Colors.white,
)),
);
},
),
alignment: Alignment.center,
);
}
@override
// TODO: implement maxExtent
double get maxExtent => 100;//最大展开高度
@override
// TODO: implement minExtent
double get minExtent => 60;//最小折叠高度
@override
bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
// TODO: implement shouldRebuild
return false;
}
}
实现效果:


更多推荐


所有评论(0)