前言一:接下來一段時間我會陸續更新一些列Flutter文字教程前端
更新進度: 因爲Mac檔期問題,可能會暫停一段時間Flutter的更新,可是會更新前端文章;vue
更新地點: 首發於公衆號,次日更新於掘金、思否、開發者頭條等地方;算法
更多交流: 能夠添加個人微信 372623326,關注個人微博:coderwhybash
但願你們能夠 幫忙轉發,點擊在看,給我更多的創做動力。微信
前言二:爲了實現界面內組件的各類排布方式,咱們須要進行佈局,和其餘端不一樣的是,Flutter中由於萬物皆Widget,因此佈局也是使用Widget來完成的。數據結構
Flutter中的佈局組件很是多,有31個用於佈局的組件,Flutter佈局組件;app
在學習的過程當中,咱們不必一個個所有掌握,掌握最經常使用的,一些特殊的組件用到時去查文檔便可;less
Flutter將佈局組件分紅了
單子佈局組件
(Single-child layout widgets) 和多子佈局組件
(Multi-child layout widgets)ide
單子佈局組件的含義是其只有一個子組件,能夠經過設置一些屬性設置該子組件所在的位置信息等。源碼分析
比較經常使用的單子佈局組件有:Align、Center、Padding、Container。
看到
Align
這個詞,咱們就知道它有咱們的對齊方式有關。在其餘端的開發中(iOS、Android、前端)Align一般只是一個屬性而已,可是Flutter中Align也是一個組件。
咱們能夠經過源碼來看一下Align有哪些屬性:
const Align({
Key key,
this.alignment: Alignment.center, // 對齊方式,默認居中對齊
this.widthFactor, // 寬度因子,不設置的狀況,會盡量大
this.heightFactor, // 高度因子,不設置的狀況,會盡量大
Widget child // 要佈局的子Widget
})
複製代碼
這裏咱們特別解釋一下widthFactor
和heightFactor
做用:
widthFactor
和heightFactor
不設置,那麼默認Align會盡量的大(儘量佔據本身所在的父組件);咱們簡單演練一下Align
class MyHomeBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Align(
child: Icon(Icons.pets, size: 36, color: Colors.red),
alignment: Alignment.bottomRight,
widthFactor: 3,
heightFactor: 3,
);
}
}
複製代碼
Center組件咱們在前面已經用過不少次了。
事實上Center組件繼承自Align,只是將alignment設置爲Alignment.center。
源碼分析:
class Center extends Align {
const Center({
Key key,
double widthFactor,
double heightFactor,
Widget child
}) : super(key: key, widthFactor: widthFactor, heightFactor: heightFactor, child: child);
}
複製代碼
咱們將上面的代碼Align換成Center
class MyHomeBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Icon(Icons.pets, size: 36, color: Colors.red),
widthFactor: 3,
heightFactor: 3,
);
}
}
複製代碼
Padding組件在其餘端也是一個屬性而已,可是在Flutter中是一個Widget,可是Flutter中沒有Margin這樣一個Widget,這是由於外邊距也能夠經過Padding來完成。
Padding一般用於設置子Widget到父Widget的邊距(你能夠稱之爲是父組件的內邊距或子Widget的外邊距)。
源碼分析:
const Padding({
Key key,
@required this.padding, // EdgeInsetsGeometry類型(抽象類),使用EdgeInsets
Widget child,
})
複製代碼
代碼演練:
class MyHomeBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(20),
child: Text(
"莫聽穿林打葉聲,何妨吟嘯且徐行。竹杖芒鞋輕勝馬,誰怕?一蓑煙雨任生平。",
style: TextStyle(
color: Colors.redAccent,
fontSize: 18
),
),
);
}
}
複製代碼
Container組件相似於其餘Android中的View,iOS中的UIView。
若是你須要一個視圖,有一個背景顏色、圖像、有固定的尺寸、須要一個邊框、圓角等效果,那麼就可使用Container組件。
Container在開發中被使用的頻率是很是高的,特別是咱們常常會將其做爲容器組件。
下面咱們來看一下Container有哪些屬性:
Container({
this.alignment,
this.padding, //容器內補白,屬於decoration的裝飾範圍
Color color, // 背景色
Decoration decoration, // 背景裝飾
Decoration foregroundDecoration, //前景裝飾
double width,//容器的寬度
double height, //容器的高度
BoxConstraints constraints, //容器大小的限制條件
this.margin,//容器外補白,不屬於decoration的裝飾範圍
this.transform, //變換
this.child,
})
複製代碼
大多數屬性在介紹其它容器時都已經介紹過了,再也不贅述,但有兩點須要說明:
width
、height
屬性來指定,也能夠經過constraints
來指定,若是同時存在時,width
、height
優先。實際上Container內部會根據width
、height
來生成一個constraints
;color
和decoration
是互斥的,實際上,當指定color時,Container內會自動建立一個decoration;decoration
屬性稍後咱們詳細學習;簡單進行一個演示:
class MyHomeBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Container(
color: Color.fromRGBO(3, 3, 255, .5),
width: 100,
height: 100,
child: Icon(Icons.pets, size: 32, color: Colors.white),
),
);
}
}
複製代碼
Container有一個很是重要的屬性 decoration
:
BoxDecoration常見屬性:
const BoxDecoration({
this.color, // 顏色,會和Container中的color屬性衝突
this.image, // 背景圖片
this.border, // 邊框,對應類型是Border類型,裏面每個邊框使用BorderSide
this.borderRadius, // 圓角效果
this.boxShadow, // 陰影效果
this.gradient, // 漸變效果
this.backgroundBlendMode, // 背景混合
this.shape = BoxShape.rectangle, // 形變
})
複製代碼
部分效果演示:
class MyHomeBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Container(
// color: Color.fromRGBO(3, 3, 255, .5),
width: 150,
height: 150,
child: Icon(Icons.pets, size: 32, color: Colors.white),
decoration: BoxDecoration(
color: Colors.amber, // 背景顏色
border: Border.all(
color: Colors.redAccent,
width: 3,
style: BorderStyle.solid
), // 這裏也可使用Border.all統一設置
// top: BorderSide(
// color: Colors.redAccent,
// width: 3,
// style: BorderStyle.solid
// ),
borderRadius: BorderRadius.circular(20), // 這裏也可使用.only分別設置
boxShadow: [
BoxShadow(
offset: Offset(5, 5),
color: Colors.purple,
blurRadius: 5
)
],
// shape: BoxShape.circle, // 會和borderRadius衝突
gradient: LinearGradient(
colors: [
Colors.green,
Colors.red
]
)
),
),
);
}
}
複製代碼
上一個章節咱們提到能夠經過 Container+BoxDecoration
來實現圓角圖像。
實現代碼以下:
class HomeContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Container(
width: 200,
height: 200,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
image: DecorationImage(
image: NetworkImage("https://tva1.sinaimg.cn/large/006y8mN6gy1g7aa03bmfpj3069069mx8.jpg"),
)
),
),
);
}
}
複製代碼
在開發中,咱們常常須要將多個Widget放在一塊兒進行佈局,好比水平方向、垂直方向排列,甚至有時候須要他們進行層疊,好比圖片上面放一段文字等;
這個時候咱們須要使用多子佈局組件(Multi-child layout widgets)。
比較經常使用的多子佈局組件是Row、Column、Stack,咱們來學習一下他們的使用。
事實上,咱們即將學習的Row組件和Column組件都繼承自Flex組件。
在學習Row和Column以前,咱們先學習主軸
和交叉軸
的概念。
由於Row是一行排布,Column是一列排布,那麼它們都存在兩個方向,而且兩個Widget排列的方向應該是對立的。
它們之中都有主軸(MainAxis)和交叉軸(CrossAxis)的概念:
Row組件用於將全部的子Widget排成一行,實際上這種佈局應該是借鑑於Web的Flex佈局。
若是熟悉Flex佈局,會發現很是簡單。
從源碼中查看Row的屬性:
Row({
Key key,
MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start, // 主軸對齊方式
MainAxisSize mainAxisSize = MainAxisSize.max, // 水平方向儘量大
CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center, // 交叉處對齊方式
TextDirection textDirection, // 水平方向子widget的佈局順序(默認爲系統當前Locale環境的文本方向(如中文、英語都是從左往右,而阿拉伯語是從右往左))
VerticalDirection verticalDirection = VerticalDirection.down, // 表示Row縱軸(垂直)的對齊方向
TextBaseline textBaseline, // 若是上面是baseline對齊方式,那麼選擇什麼模式(有兩種可選)
List<Widget> children = const <Widget>[],
})
複製代碼
部分屬性詳細解析:(不過文字是真的難描述,後續推出視頻學習較差)
mainAxisSize:
MainAxisSize.max
,表示儘量多的佔用水平方向的空間,此時不管子widgets實際佔用多少水平空間,Row的寬度始終等於水平方向的最大寬度MainAxisSize.min
表示儘量少的佔用水平空間,當子widgets沒有佔滿水平剩餘空間,則Row的實際寬度等於全部子widgets佔用的的水平空間;mainAxisAlignment:表示子Widgets在Row所佔用的水平空間內對齊方式
MainAxisSize.min
,則此屬性無心義,由於子widgets的寬度等於Row的寬度MainAxisSize.max
時,此屬性纔有意義MainAxisAlignment.start
表示沿textDirection的初始方向對齊,TextDirection.ltr
時,則MainAxisAlignment.start
表示左對齊,textDirection取值爲TextDirection.rtl
時表示從右對齊。MainAxisAlignment.end
和MainAxisAlignment.start
正好相反;MainAxisAlignment.center
表示居中對齊。crossAxisAlignment:表示子Widgets在縱軸方向的對齊方式
start
、end
、 center
三個值)VerticalDirection.down
時crossAxisAlignment.start
指頂部對齊,verticalDirection值爲VerticalDirection.up
時,crossAxisAlignment.start
指底部對齊;而crossAxisAlignment.end
和crossAxisAlignment.start
正好相反;咱們來對部分屬性進行簡單的代碼演練,其餘一些屬性你們本身學習一下
class MyHomeBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Container(color: Colors.red, width: 60, height: 60),
Container(color: Colors.blue, width: 80, height: 80),
Container(color: Colors.green, width: 70, height: 70),
Container(color: Colors.orange, width: 100, height: 100),
],
);
}
}
複製代碼
默認狀況下,Row會盡量佔據多的寬度,讓子Widget在其中進行排布,這是由於mainAxisSize屬性默認值是MainAxisSize.max
。
咱們來看一下,若是這個值被修改成MainAxisSize.max
會什麼變化:
關於TextBaseline的取值解析
若是咱們但願紅色和黃色的Container Widget不要設置固定的寬度,而是佔據剩餘的部分,這個時候應該如何處理呢?
這個時候咱們可使用 Expanded
來包裹 Container Widget,而且將它的寬度不設置值;
class MyHomeBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Expanded(
flex: 1,
child: Container(color: Colors.red, height: 60),
),
Container(color: Colors.blue, width: 80, height: 80),
Container(color: Colors.green, width: 70, height: 70),
Expanded(
flex: 1,
child: Container(color: Colors.orange, height: 100),
)
],
);
}
}
複製代碼
Column組件用於將全部的子Widget排成一列,學會了前面的Row後,Column只是和row的方向不一樣而已。
咱們直接看它的源碼:咱們發現和Row屬性是一致的,再也不解釋
Column({
Key key,
MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
MainAxisSize mainAxisSize = MainAxisSize.max,
CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
TextDirection textDirection,
VerticalDirection verticalDirection = VerticalDirection.down,
TextBaseline textBaseline,
List<Widget> children = const <Widget>[],
})
複製代碼
咱們直接將Row的代碼中Row改成Column,查看代碼運行效果:
class MyHomeBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Expanded(
flex: 1,
child: Container(color: Colors.red, width: 60),
),
Container(color: Colors.blue, width: 80, height: 80),
Container(color: Colors.green, width: 70, height: 70),
Expanded(
flex: 1,
child: Container(color: Colors.orange, width: 100),
)
],
);
}
}
複製代碼
在開發中,咱們多個組件頗有可能須要重疊顯示,好比在一張圖片上顯示文字或者一個按鈕等。
在Android中可使用Frame來實現,在Web端可使用絕對定位,在Flutter中咱們須要使用層疊佈局Stack。
咱們仍是經過源碼來看一下Stack有哪些屬性:
Stack({
Key key,
this.alignment = AlignmentDirectional.topStart,
this.textDirection,
this.fit = StackFit.loose,
this.overflow = Overflow.clip,
List<Widget> children = const <Widget>[],
})
複製代碼
參數j解析:
TextDirection.ltr
,則alignment的start
表明左,end
表明右;textDirection的值爲TextDirection.rtl
,則alignment的start
表明右,end
表明左。StackFit.loose
表示使用子widget的大小,StackFit.expand
表示擴伸到Stack的大小。Overflow.clip
時,超出部分會被剪裁(隱藏),值爲Overflow.visible
時則不會。Stack會常常和Positioned一塊兒來使用,Positioned能夠決定組件在Stack中的位置,用於實現相似於Web中的絕對定位效果。
咱們來看一個簡單的演練:
class MyHomeBody extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
Container(
color: Colors.purple,
width: 300,
height: 300,
),
Positioned(
left: 20,
top: 20,
child: Icon(Icons.favorite, size: 50, color: Colors.white)
),
Positioned(
bottom: 20,
right: 20,
child: Text("你好啊,李銀河", style: TextStyle(fontSize: 20, color: Colors.white)),
)
],
);
}
}
複製代碼
**注意:**Positioned組件只能在Stack中使用。
備註:全部內容首發於公衆號,以後除了Flutter也會更新其餘技術文章,TypeScript、React、Node、uniapp、mpvue、數據結構與算法等等,也會更新一些本身的學習心得等,歡迎你們關注