和谐学习!不急不躁!!我是你们的老朋友小青龙~
前言
Flutter 布局的三种方法:Row
、Cloumn
、Stack
,分别对应着 横向布局
、纵向布局
、叠加布局
。
下面我们将按照图文结合的方式一一介绍。
本文目录
-
主轴(MainAxisAlignment)
-
Row -- 横向布局
-
Cloumn -- 纵向布局
-
Stack -- 叠加布局
-
交叉轴(crossAxisAlignment)
-
Expanded组件
-
调整位置之Positioned和magrin
-
width、height和AspectRatio
-
补充
1、主轴(MainAxisAlignment)
在介绍Row、Cloumn之前,我们先了解一个概念--- 主轴
啥是主轴呢?你可以理解成,三位坐标轴上,三个带方向箭头的轴(x轴、y轴、z轴)。【如图👇】
如果不是很好理解,后面我会结合例子给大家做进一步的解释。让我们继续往下看~
2、Row -- 横向布局
2.1、不设置主轴方向,默认贴近左边(MainAxisAlignment.start
)
先上效果图
核心代码
class WidgetRow extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
children: [
Container(
child: const Icon(Icons.build,size: 80,),
color: Colors.yellow,
),
Container(
child: const Icon(Icons.ac_unit,size: 80,),
color: Colors.blue,
),
Container(
child: const Icon(Icons.access_alarm,size: 80,),
color: Colors.deepOrangeAccent,
),
],
);
}
}
2.2、设置主轴方向,贴近右边 (MainAxisAlignment.end
)
2.3、设置主轴方向,居中 (MainAxisAlignment.center
)
2.4、设置主轴方向,间距等分(MainAxisAlignment.spaceBetween
)
即,将几个子部件放到屏幕上以后,剩下的空间平均分配到子部件们之间。
如图所示:第一个子部件和最后一个部件与两端间距为0,因为两周没有其它子部件。
2.5、设置主轴方向,环绕等分(MainAxisAlignment.spaceAround
)
spaceAround,顾名思义就是环绕四周。
就是将几个子部件放到屏幕上以后,剩下的空间平均分配到每个子部件四周。
为了更清晰的表达,我这边又画了一个图:
2.6、设置主轴方向,剩余空间等分(MainAxisAlignment.spaceEvenly
)
spaceEvenly,即将几个子部件放到屏幕上以后,剩下的空间平均等分。
参与剩余空间等分的成员包括:所有子部件之间的间距、第一个子部件和最后一个部件与两端间距。
为了更清晰的表达,我这边又画了一个图:
3、Cloumn -- 纵向布局
3.1、不设置主轴方向,默认贴近上边(MainAxisAlignment.start
)
核心代码
class WidgetColumn extends StatelessWidget {
const WidgetColumn({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Column(
children: [
Container(
child: const Icon(Icons.build,size: 80,),
color: Colors.yellow,
),
Container(
child: const Icon(Icons.ac_unit,size: 80,),
color: Colors.blue,
),
Container(
child: const Icon(Icons.access_alarm,size: 80,),
color: Colors.deepOrangeAccent,
),
],
);
}
}
3.2、设置主轴方向,贴近右边 (MainAxisAlignment.end
)
3.3、设置主轴方向,居中 (MainAxisAlignment.center
)
3.4、设置主轴方向,间距等分(MainAxisAlignment.spaceBetween
)
3.5、设置主轴方向,环绕等分(MainAxisAlignment.spaceAround
)
3.6、设置主轴方向,剩余空间等分(MainAxisAlignment.spaceEvenly
)
4、Stack -- 叠加布局
4.1、简单的叠加
代码
class WidgetStack extends StatelessWidget {
const WidgetStack({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
alignment: const Alignment(0,0),
child: Stack(
children: [
Container(
alignment: const Alignment(0, 0),
child: const Icon(Icons.build,size: 80,),
color: Colors.yellow,
height: 300,
width:300,
),
Container(
child: const Icon(Icons.ac_unit,size: 80,),
color: Colors.blue,
height: 200,
width:200,
),
Container(
child: const Icon(Icons.access_alarm,size: 80,),
color: Colors.deepOrangeAccent,
height: 100,
width:100,
),
],
),
) ;
}
}
4.2、叠加 + 调整位置(Positioned
)
如果想在叠加的基础上,还想对子部件进行位置的调整,可以考虑对子部件套一个`Positioned`。
4.3、叠加 + 中心对齐(Positioned
)
如果想实现多个子部件叠加,且要求它们的中心的对齐,可以用`Positioned`组件嵌套。
并设置left、top、right、bottom四个数值
注意:当同一轴上两个方向都设置了约束,那么对应的宽或高就不需要设置了
例如,已经设置了Positioned的right和left,那么就不需要设置子部件width,因为这样会导致冲突而报错。
5、交叉轴(crossAxisAlignment
)
5.1、什么是交叉轴
交叉轴,顾名思义就是和主轴垂直交叉的轴。比如
-
Row
是水平布局,那么交叉轴
指的就是纵向那条轴
-
Cloumn
是纵向布局,那么交叉轴
指的就是横向那条轴
5.2、交叉轴的值分别有什么效果
交叉轴CrossAxisAlignment
有这么几个选项值
start
Row布局, (交叉轴)纵向靠上👆
Cloumn布局,(交叉轴)横向靠左👈
为了得到明显的效果,给子部件设置大小不一样。
end
Row布局, (交叉轴)纵向靠下👇
Cloumn布局,(交叉轴)横向靠右👉
center
Row布局, (交叉轴)纵向居中
Cloumn布局,(交叉轴)横向居中
stretch
Row布局, (交叉轴)纵向拉伸
Cloumn布局,(交叉轴)横向拉伸
baseline
这个有点特殊,如果你单独设置,那是会报错的
从控制台我们可以得知,原来错误的原因是少设置了一个属性textBaseline
改完之后再运行
运行是可以了,但是这个效果似乎...和end一样,那么有什么用呢?
既然它叫TextBaseline
,开头单词是Text
,那么是否和Text
有关系呢?
我们把子部件由Icon
换成Text
纵向布局,效果是这样的
经测试发现,TextBaseline
对应着文本对齐,在某些业务场合会需要这样布局。
6、Expanded组件
6.1、交叉轴的特性
Expanded
的特性是在主轴方法会自动填充剩余空间
,且当同一层级,存在多个
Expanded时,会均分剩余空间
:
如上图所示,Row横向布局,除去最左侧固定的50像素*50像素大小(灰色),剩余的空间被3个Expanded均分。
利用这个特性,我们可以实现把某一区域进行几等分。
6.2、Expanded之flex
属性
Expanded有个flex属性,作用有点类似于细胞分裂,无论分成多少份,最终每个细胞都是一样大小,如果某个Expanded拆分成3份,那么最终显示的时候,3份占用的大小就是这个Expanded的实际大小。
举个例子:
class WidgetRow extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
children: [
Container(width: 50,height: 50,color: Colors.grey,),
Expanded(child: Container(
child: const Text('标题',style: TextStyle(fontSize: 10),),
color: Colors.yellow,
),flex: 1,),
Expanded(child: Container(
child: const Text('今日头条',style: TextStyle(fontSize: 14),),
color: Colors.blue,
),flex: 2),
Expanded(child: Container(
child: const Text('国庆长假',style: TextStyle(fontSize: 18),),
color: Colors.deepOrangeAccent,
),flex: 3),
],
mainAxisAlignment: MainAxisAlignment.spaceEvenly,//主轴方向
crossAxisAlignment:CrossAxisAlignment.baseline,//交叉轴填充
textBaseline: TextBaseline.alphabetic,
);
}
}
利用这个特性,我们可以实现对某一区域等比划分。
7、调整位置之Positioned
和magrin
class WidgetStack01 extends StatelessWidget {
const WidgetStack01({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
//
return Container(
alignment: const Alignment(0,0),
child: Stack(
children: [
Container(
alignment: const Alignment(0, 0),
child: const Icon(Icons.build,size: 80,),
color: Colors.yellow,
height: 300,
width:300,
),
Container(
child: const Icon(Icons.ac_unit,size: 80,),
color: Colors.blue,
height: 200,
width: 200,
),
Container(
child: const Icon(Icons.access_alarm,size: 80,),
color: Colors.deepOrangeAccent,
height: 100,
width:100,
),
],
),
) ;
}
}
运行效果如下
要求:红色方块距离黄色方块20像素,请问如何实现?
光看图,我们会想到用margin,设置右边距20像素,一顿操作下来发现没效果
原因是margin是相对父视图,而黄色方块不是红色方块的父视图,所以这个方案行不通。
于是我们想到了Positioned,修改后代码如下
要注意的是,套上Positioned
之后,Positioned
的right
和Container
的margin
都会生效,所以应该把margin这行代码
注释掉。
8、width、height和AspectRatio
class AspectRatioDemo extends StatelessWidget {
const AspectRatioDemo({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
alignment: const Alignment(0,0),
color: Colors.blueGrey,
child: Container(
color: Colors.deepOrangeAccent,
// width: 100,
height: 100,
child: AspectRatio(
aspectRatio: 1/2,
child: Container(
color: Colors.green,
width: 200,
height: 200,
),
),
),
);
}
}
上面3个图告诉我们:
-
AspectRatio会作用于当前Container
-
当
width
、height
和AspectRatio
三者共存时,AspectRatio不会起作用。
上图告诉我们:
AspectRatio
会对它子部件
造成影响,且AspectRatio的子部件设置width
、height
均无效。
结论:AspectRatio最好是用于设置当前Container的宽高比。
9、补充
main.dart
代码
import 'package:flutter/material.dart';
import 'other_components/layout_demo.dart';
void main() {
// 演示各种组建
runApp(ComponentsApp());
}
// 演示组件
class ComponentsApp extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return MaterialApp(
home:Scaffold(
appBar:AppBar(
title:const Text("演示组件"),
),
body: LayoutDemo(),
),
);
}
}
layout_demo.dart
部分代码
import 'dart:ui';
import 'package:flutter/material.dart';
class LayoutDemo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
color: Colors.brown,
child: WidgetRow(), //这个WidgetRow根据情况替换
);
}
}