因為筆者本身主要從事是Android開發(fā),所以很多角度都是作為一個Android開發(fā)者學(xué)習(xí)Flutter的角度出發(fā)旋圆,IOS或者H5的開發(fā)同學(xué)可以選擇性閱讀
目錄
前言
在前兩節(jié)介紹了Flutter(一)StatelessWidget基礎(chǔ)組件和Flutter(二)StatefulWidget基礎(chǔ)組件顽馋,在了解了一些基礎(chǔ)組件的用法后见芹,我們需要將組件放置到我們預(yù)期的位置上叮叹,或者希望對組件進行裁剪宙项、設(shè)置透明度等罕邀,這時候就需要用到Flutter布局組件畅形。本節(jié)就來介紹一些Flutter常用布局組件
Opacity
Opacity
就是用來設(shè)置透明度的組件
使用方法(給圖片設(shè)置60%透明度)
Opacity(
opacity: 0.6,
child: Image.network(
"https://upload-images.jianshu.io/upload_images/10992781-a64bd14d27699266.png?imageMogr2/auto-orient/strip|imageView2/2/w/800/format/webp",
height: 100,
width: 100,
fit: BoxFit.fill,
))
ClipOval
ClipOval
是用于圓形裁剪的組件
使用方法
ClipOval(
child: SizedBox(
height: 100,
width: 100,
child: Image.network(
"https://upload-images.jianshu.io/upload_images/10992781-a64bd14d27699266.png?imageMogr2/auto-orient/strip|imageView2/2/w/800/format/webp",
fit: BoxFit.fill,
),
),
)
ClipRRect
ClipRRect
是用于圓角矩形裁剪的組件
使用方法
ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Image.network(
"https://upload-images.jianshu.io/upload_images/10992781-a64bd14d27699266.png?imageMogr2/auto-orient/strip|imageView2/2/w/800/format/webp",
height: 100,
width: 100,
fit: BoxFit.fill,
),
)
PhysicalModel
PhysicalModel
也是用于圓角矩形裁剪的組件,PhysicalModel
跟ClipRRect
的區(qū)別在于它可以設(shè)置z軸和陰影诉探,其他效果跟ClipRRect
基本一致
PhysicalModel(
elevation: 10,
shadowColor: Colors.redAccent,
color: Colors.transparent,
clipBehavior: Clip.antiAlias,
borderRadius: BorderRadius.circular(10),
child: Image.network(
"https://upload-images.jianshu.io/upload_images/10992781-a64bd14d27699266.png?imageMogr2/auto-orient/strip|imageView2/2/w/800/format/webp",
height: 100,
width: 100,
fit: BoxFit.fill,
),
)
Align(Center)
Center
顧名思義就是一個用于居中對齊的布局組件
Center(
child: Text("Center"),
))
Padding
Padding
用法很純粹日熬,就是用來給組件設(shè)置padding的
Container(
height: 100,
width: 100,
decoration: BoxDecoration(color: Colors.blueAccent),
child: Padding(
padding: EdgeInsets.all(10),
child: Container(
decoration: BoxDecoration(color: Colors.redAccent),
),
))
SizedBox
SizedBox
的用法也很純粹,用來指定組件的繪制區(qū)域大小阵具,其實Padding
和SizedBox
都可以用Container
代替
SizedBox(
height: 100,
width: 100,
child: Image.network(
"https://upload-images.jianshu.io/upload_images/10992781-a64bd14d27699266.png?imageMogr2/auto-orient/strip|imageView2/2/w/800/format/webp",
fit: BoxFit.fill,
),
)
FractionallySizedBox
FractionallySizedBox
又稱為百分比布局碍遍,可以通過widthFactor
或heightFactor
設(shè)置空間的寬高占比
默認情況不使用FractionallySizedBox
Container(
margin: EdgeInsets.only(top: 10),
decoration: BoxDecoration(color: Colors.lightGreen),
child: Text("寬度撐滿"),
)
使用FractionallySizedBox
FractionallySizedBox(
//寬度撐滿
widthFactor: 1,
child: Container(
margin: EdgeInsets.only(top: 10),
decoration: BoxDecoration(color: Colors.lightGreen),
child: Text("寬度撐滿"),
),
)
Stack
Stack
類似于Android中的FrameLayout
Stack(
children: <Widget>[
Container(
height: 100,
width: 100,
decoration: BoxDecoration(color: Colors.red),
),
Container(
height: 50,
width: 50,
decoration: BoxDecoration(color: Colors.blue),
)
],
)
Flex(Column)
Column
類似于Android中的垂直線性布局
Column(
children: <Widget>[
Container(
height: 100,
decoration: BoxDecoration(color: Colors.red),
),
Container(
height: 100,
decoration: BoxDecoration(color: Colors.blue),
)
],
)
Flex(Row)
Column
類似于Android中的水平的線性布局
Row(
children: <Widget>[
Container(
width: 100,
height: 100,
decoration: BoxDecoration(color: Colors.red),
),
Container(
width: 100,
height: 100,
decoration: BoxDecoration(color: Colors.blue),
)
],
)
Wrap
Wrap
常用的地方一般是熱搜詞定铜,每行會自動計算保證能夠撐滿,如果無法撐滿怕敬,則會進行換行
Wrap(
alignment: WrapAlignment.start,//主軸方向上的對齊方式揣炕,默認為start。
runAlignment: WrapAlignment.end,//run的對齊方式东跪。run可以理解為新的行或者列畸陡,如果是水平方向布局的話,run可以理解為新的一行
spacing: 10, //主軸方向上的間距
runSpacing: 5,//run的間距
children: <Widget>[
Chip(
avatar: Icon(Icons.photo),
label: Text("option1"),
),
Chip(
avatar: Icon(Icons.photo),
label: Text("option2"),
),
Chip(
avatar: Icon(Icons.photo),
label: Text("option3"),
),
Chip(
avatar: Icon(Icons.photo),
label: Text("option4"),
),
Chip(
avatar: Icon(Icons.photo),
label: Text("option5"),
),
Chip(
avatar: Icon(Icons.photo),
label: Text("option6"),
),
Chip(
avatar: Icon(Icons.photo),
label: Text("option7"),
)
],
)
Flow
我們一般很少會使用Flow
虽填,因為其過于復(fù)雜丁恭,需要自己實現(xiàn)子widget的位置轉(zhuǎn)換,在很多場景下首先要考慮的是Wrap
是否滿足需求斋日。Flow
主要用于一些需要自定義布局策略或性能要求較高(如動畫中)的場景牲览。Flow
有如下優(yōu)點:
- 性能好;
Flow
是一個對子組件尺寸以及位置調(diào)整非常高效的控件恶守,Flow
用轉(zhuǎn)換矩陣在對子組件進行位置調(diào)整的時候進行了優(yōu)化:在Flow
定位過后第献,如果子組件的尺寸或者位置發(fā)生了變化,在FlowDelegate
中的paintChildren()
方法中調(diào)用context.paintChild
進行重繪兔港,而context.paintChild
在重繪時使用了轉(zhuǎn)換矩陣庸毫,并沒有實際調(diào)整組件位置。 - 靈活衫樊;由于我們需要自己實現(xiàn)
FlowDelegate
的paintChildren()
方法飒赃,所以我們需要自己計算每一個組件的位置,因此科侈,可以自定義布局策略载佳。
缺點:
- 使用復(fù)雜。
- 不能自適應(yīng)子組件大小臀栈,必須通過指定父容器大小或?qū)崿F(xiàn)
TestFlowDelegate
的getSize
返回固定大小刚盈。
示例:
我們對六個色塊進行自定義流式布局:
Flow(
delegate: TestFlowDelegate(margin: EdgeInsets.all(10.0)),
children: <Widget>[
new Container(width: 80.0, height:80.0, color: Colors.red,),
new Container(width: 80.0, height:80.0, color: Colors.green,),
new Container(width: 80.0, height:80.0, color: Colors.blue,),
new Container(width: 80.0, height:80.0, color: Colors.yellow,),
new Container(width: 80.0, height:80.0, color: Colors.brown,),
new Container(width: 80.0, height:80.0, color: Colors.purple,),
],
)
實現(xiàn)TestFlowDelegate:
class TestFlowDelegate extends FlowDelegate {
EdgeInsets margin = EdgeInsets.zero;
TestFlowDelegate({this.margin});
@override
void paintChildren(FlowPaintingContext context) {
var x = margin.left;
var y = margin.top;
//計算每一個子widget的位置
for (int i = 0; i < context.childCount; i++) {
var w = context.getChildSize(i).width + x + margin.right;
if (w < context.size.width) {
context.paintChild(i,
transform: new Matrix4.translationValues(
x, y, 0.0));
x = w + margin.left;
} else {
x = margin.left;
y += context.getChildSize(i).height + margin.top + margin.bottom;
//繪制子widget(有優(yōu)化)
context.paintChild(i,
transform: new Matrix4.translationValues(
x, y, 0.0));
x += context.getChildSize(i).width + margin.left + margin.right;
}
}
}
@override
getSize(BoxConstraints constraints){
//指定Flow的大小
return Size(double.infinity,200.0);
}
@override
bool shouldRepaint(FlowDelegate oldDelegate) {
return oldDelegate != this;
}
}
可以看到我們主要的任務(wù)就是實現(xiàn)paintChildren
,它的主要任務(wù)是確定每個子widget位置挂脑。由于Flow不能自適應(yīng)子widget的大小藕漱,我們通過在getSize
返回一個固定大小來指定Flow的大小
Positioned
Positioned
用于指定組件的具體位置
Stack(
children: <Widget>[
Container(
height: 100,
width: 100,
decoration: BoxDecoration(color: Colors.red),
),
Positioned(
top: 50,
left: 50,
child: Container(
height: 50,
width: 50,
decoration: BoxDecoration(color: Colors.blue),
),
)
],
)
Flexible(Expand)
Expanded
組件可以使Row
、Column
崭闲、Flex
的子組件在其主軸方向上展開并填充可用空間肋联。如果多個子組件展開,可用空間會被其flex
屬性按比例分割(類似于在Andorid中給線性布局設(shè)置weight
)
注意:
Expanded
組件必須用在Row
刁俭、Column
橄仍、Flex
及其子組件內(nèi)使用,并且從Expanded
到封裝它的Row、Column侮繁、Flex的路徑必須只包括StatelessWidgets
或StatefulWidgets
組件(不能是其他類型的組件虑粥,像RenderObjectWidget
,它是渲染對象宪哩,不再改變尺寸了娩贷,因此Expanded
不能放進RenderObjectWidget
)
Column(
children: <Widget>[
Expanded(
flex: 1,
child: Container(
decoration: BoxDecoration(color: Colors.redAccent),
child: Text("Expanded1"),
)),
Expanded(
flex: 2,
child: Container(
decoration: BoxDecoration(color: Colors.blueAccent),
child: Text("Expanded2"),
))
],
)