🔥 本文由 程序喵正在路上 原創,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),),),),]);}
}