🔥 本文由 程序喵正在路上 原创,CSDN首发!
💖 系列专栏:Flutter学习
🌠 首发时间:2024年5月25日
🦋 欢迎关注🖱点赞👍收藏🌟留言🐾
目录
- 线性布局
- Row水平布局组件
- Column垂直布局组件
- double.infinity和double.maxFinite
- 弹性布局
- 水平弹性布局
- 垂直弹性布局
- 使用Row或Column结合Expanded实现案例
- 层叠布局
- Stack组件
- Align组件
- Positioned组件
- MediaQuery获取屏幕宽度和高度
- Stack结合Positioned固定导航案例
线性布局
Row水平布局组件
实现如下效果:
因为图中有3个差不多的盒子,都是一个盒子里面放一个图标,所以我们将其写成了一个类 IconContainer,以减少代码量
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
Widget build(BuildContext context) {
return Container(
height: double.infinity, //无穷大
width: double.infinity,
color: Colors.white,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
IconContainer(Icons.home, color: Colors.red),
IconContainer(Icons.search),
IconContainer(Icons.send, color: Colors.orange),
],
),
);
}
}
class IconContainer extends StatelessWidget {
Color color; //盒子颜色
double iconSize; //图标大小
IconData icon; //图标
//盒子默认为蓝色,图标大小默认32
IconContainer(this.icon,
{super.key, this.color = Colors.blue, this.iconSize = 32});
Widget build(BuildContext context) {
return Container(
height: 100,
width: 100,
color: color,
//图标颜色默认为白色
child: Center(child: Icon(icon, size: iconSize, color: Colors.white)),
);
}
}
Column垂直布局组件
实现如下效果:
将前面代码中的 Row 改成 Column 即可:
double.infinity和double.maxFinite
double.infinity
和 double.maxFinite
可以让当前元素的 width
或者 height
达到父元素的尺寸
如下可以让Container铺满整个屏幕
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
Widget build(BuildContext context) {
return Container(
height: double.infinity,
width: double.infinity,
color: Colors.white,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
IconContainer(Icons.home, color: Colors.red),
IconContainer(Icons.search),
IconContainer(Icons.send, color: Colors.orange),
],
),
);
}
}
如下可以让Container的宽度和高度等于父元素的宽度高度
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
Widget build(BuildContext context) {
return Container(
height: 500,
width: 300,
color: Colors.red,
child: Container(
height: double.maxFinite,
width: double.infinity,
color: Colors.yellow,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
IconContainer(Icons.home, color: Colors.red),
IconContainer(Icons.search),
IconContainer(Icons.send, color: Colors.orange),
],
),
),
);
}
}
弹性布局
水平弹性布局
Flex
组件可以沿着水平或垂直方向排列子组件,如果你知道主轴方向,使用 Row
或 Column
会方便一些,因为 Row
和 Column
都继承自 Flex
,参数基本相同,所以能使用 Flex
的地方基本上都可以使用 Row
或 Column
。 Flex
本身功能是很强大的,它也可以和 Expanded
组件配合实现弹性布局 。
实现如下效果:
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
Widget build(BuildContext context) {
return Flex(
direction: Axis.horizontal, //水平方向
children: [
//flex: 占用多少位置
Expanded(flex: 2, child: IconContainer(Icons.home, color: Colors.red)),
Expanded(flex: 1, child: IconContainer(Icons.search))
],
);
}
}
class IconContainer extends StatelessWidget {
Color color; //盒子颜色
double iconSize; //图标大小
IconData icon; //图标
//盒子默认为蓝色,图标大小默认32
IconContainer(this.icon,
{super.key, this.color = Colors.blue, this.iconSize = 32});
Widget build(BuildContext context) {
return Container(
height: 100,
width: 100,
color: color,
//图标颜色默认为白色
child: Center(child: Icon(icon, size: iconSize, color: Colors.white)),
);
}
}
因为 Row
继承自 Flex
,所以我们将代码中的 Flex
换成 Row
,同样是可以的,而且我们还不用设置方向。当我们能确定主轴的方向时,推荐使用 Row
和 Column
。
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
Widget build(BuildContext context) {
return Row(
children: [
//flex: 占用多少位置
Expanded(flex: 2, child: IconContainer(Icons.home, color: Colors.red)),
Expanded(flex: 1, child: IconContainer(Icons.search))
],
);
}
}
垂直弹性布局
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
Widget build(BuildContext context) {
return Column(
children: [
//flex: 占用多少位置
Expanded(flex: 2, child: IconContainer(Icons.home, color: Colors.red)),
Expanded(flex: 1, child: IconContainer(Icons.search))
],
);
}
}
使用Row或Column结合Expanded实现案例
实现如下效果:
class MyHomePage extends StatelessWidget {
const MyHomePage({super.key});
Widget build(BuildContext context) {
return ListView(
children: [
Container(
width: double.infinity,
height: 200,
color: Colors.blue,
child: Image.network(
"https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/1.jpg",
fit: BoxFit.cover)),
const SizedBox(height: 10),
Row(
children: [
Expanded(
flex: 2,
child: SizedBox(
height: 200,
child: Image.network(
"https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/2.jpg",
fit: BoxFit.cover),
)),
const SizedBox(width: 10),
Expanded(
flex: 1,
child: SizedBox(
height: 200,
child: Column(
children: [
Expanded(
flex: 1,
child: SizedBox(
width: double.infinity,
child: Image.network(
"https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/3.jpg",
fit: BoxFit.cover),
),
),
const SizedBox(height: 10),
Expanded(
flex: 1,
child: SizedBox(
width: double.infinity,
child: Image.network(
"https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/4.jpg",
fit: BoxFit.cover),
),
),
],
),
),
),
],
),
const SizedBox(height: 10),
Row(
children: [
Expanded(
flex: 1,
child: SizedBox(
height: 200,
child: Column(children: [
Expanded(
flex: 1,
child: SizedBox(
width: double.infinity,
child: Image.network(
"https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/5.jpg",
fit: BoxFit.cover),
),
),
const SizedBox(height: 10),
Expanded(
flex: 1,
child: SizedBox(
width: double.infinity,
child: Image.network(
"https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/6.jpg",
fit: BoxFit.cover),
),
),
]),
),
),
const SizedBox(width: 10),
Expanded(
flex: 1,
child: SizedBox(
height: 200,
child: Column(children: [
Expanded(
flex: 1,
child: SizedBox(
width: double.infinity,
child: Image.network(
"https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/7.jpg",
fit: BoxFit.cover),
),
),
const SizedBox(height: 10),
Expanded(
flex: 1,
child: SizedBox(
width: double.infinity,
child: Image.network(
"https://xixi-web-tlias.oss-cn-guangzhou.aliyuncs.com/8.jpg",
fit: BoxFit.cover),
),
),
]),
),
),
],
),
],
);
}
}
层叠布局
Stack组件
Stack 表示堆的意思,我们可以用 Stack 或者 Stack 结合 Align 或者 Stack 结合 Positiond 来实现页面的定位布局
属性 | 说明 |
---|---|
alignment | 配置所有子元素的显示位置 |
children | 子组件 |
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return Center(
child: Stack(
alignment: Alignment.topLeft,
children: [
Container(
height: 400,
width: 300,
color: Colors.blue,
),
const Text(
"我是一个文本",
style: TextStyle(fontSize: 40, color: Colors.white),
)
],
),
);
}
}
Align组件
Align 组件可以调整子组件的位置 , Stack 组件中结合 Align 组件也可以控制每个子元素的显示位置
属性 | 说明 |
---|---|
alignment | 配置所有子元素的显示位置 |
child | 子组件 |
Align结合Container的使用
我们先来看一个简单的例子:
FlutterLogo
是Flutter SDK 提供的一个组件,内容就是 Flutter 的 log
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return Container(
height: 300,
width: 300,
color: Colors.blue.shade50,
child: const Align(
alignment: Alignment.topRight,
child: FlutterLogo(
size: 100,
),
),
);
}
}
Align结合Container的使用
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return Container(
height: 300,
width: 300,
color: Colors.blue.shade50,
child: const Align(
alignment: Alignment(2, 0),
child: FlutterLogo(
size: 100,
),
),
);
}
}
Alignment Widget 会以矩形的中心点作为坐标原点,即 Alignment(0, 0)
。 x 、y 的值从 -1 到 1 分别代表矩形左边到右边的距离和顶部到底边的距离,因此 2 个水平(或垂直)单位则等于矩形的宽(或高),如 Alignment(-1, -1)
代表矩形的左侧顶点,而 Alignment(1, 1)
代表右侧底
部终点,而 Alignment(1, -1)
则正是右侧顶点,即 Alignment.topRight
。为了使用方便,矩形的原点、四个顶点,以及四条边的终点在 Alignment
类中都已经定义为了静态常量。
Alignment
可以通过其坐标转换公式将其坐标转为子元素的具体偏移坐标:
(Alignment.x*childWidth/2+childWidth/2, Alignment.y*childHeight/2+childHeight/2)
其中 childWidth
为子元素的宽度, childHeight
为子元素高度。
现在我们再看看上面的示例,我们将 Alignment(2, 0)
带入上面公式,
(
2
×
300
/
2
+
300
/
2
,
0
×
300
/
2
+
300
/
2
)
(2 \times 300 / 2 + 300 / 2, 0 \times 300 / 2 + 300 / 2)
(2×300/2+300/2,0×300/2+300/2) ,可得 FlutterLogo
的实际偏移坐标正是 (450,150)
。
Align结合Stack的使用
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return Center(
child: Container(
height: 400,
width: 300,
color: Colors.blue,
child: const Stack(
children: [
Align(
alignment: Alignment(1, -0.2),
child: Icon(Icons.home, size: 40, color: Colors.white),
),
Align(
alignment: Alignment.center,
child: Icon(Icons.search, size: 30, color: Colors.white),
),
Align(
alignment: Alignment.bottomRight,
child: Icon(Icons.settings_applications,
size: 30, color: Colors.white),
),
],
),
),
);
}
}
Positioned组件
Stack
组件中结合 Positioned
组件也可以控制每个子元素的显示位置
属性 | 说明 |
---|---|
top | 子元素距离顶部的距离 |
bottom | 子元素距离底部的距离 |
left | 子元素距离左侧距离 |
right | 子元素距离右侧距离 |
child | 子组件 |
width | 组件的高度(注意:宽度和高度必须是固定值,不能使用 double.infinity ) |
height | 子组件的高度 |
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return Center(
child: Container(
height: 400,
width: 300,
color: Colors.blue,
child: const Stack(
children: [
Positioned(
left: 10,
child: Icon(Icons.home, size: 40, color: Colors.white),
),
Positioned(
bottom: 0,
left: 100,
child: Icon(Icons.search, size: 30, color: Colors.white),
),
Positioned(
right: 0,
child: Icon(Icons.settings_applications,
size: 30, color: Colors.white),
),
],
),
),
);
}
}
MediaQuery获取屏幕宽度和高度
前面说到 Positioned
组件的高度和宽度不能使用 double.infinity
,在这种情况下,如果我们可以在组件的 build
方法中可以通过 MediaQuery.of(context).size;
来设置
final size = MediaQuery.of(context).size;
final width = size.width;
final height = size.height;
Stack结合Positioned固定导航案例
实现如下效果:
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
return Stack(children: [
ListView(
padding: const EdgeInsets.only(top: 45),
children: const [
ListTile(
title: Text("我是标题1"),
),
ListTile(
title: Text("我是标题2"),
),
ListTile(
title: Text("我是标题3"),
),
ListTile(
title: Text("我是标题4"),
),
ListTile(
title: Text("我是标题5"),
),
ListTile(
title: Text("我是标题6"),
),
ListTile(
title: Text("我是标题7"),
),
ListTile(
title: Text("我是标题8"),
),
ListTile(
title: Text("我是标题9"),
),
ListTile(
title: Text("我是标题10"),
),
ListTile(
title: Text("我是标题11"),
),
ListTile(
title: Text("我是标题12"),
),
ListTile(
title: Text("我是标题13"),
),
ListTile(
title: Text("我是标题14"),
),
ListTile(
title: Text("我是标题15"),
),
],
),
Positioned(
top: 0,
left: 0,
height: 40,
width: size.width,
child: Container(
alignment: Alignment.center,
color: Colors.blue,
child: const Text(
"二级导航",
style: TextStyle(color: Colors.white),
),
),
),
]);
}
}