🔥 本文由 程序喵正在路上 原创,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; //图标//盒子默认为蓝色,图标大小默认32IconContainer(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; //图标//盒子默认为蓝色,图标大小默认32IconContainer(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),),),),]);}
}