前面的文章,我們說了:
線性佈局---Flutter 佈局控制元件篇-->Row、Column
彈性佈局---Flutter 佈局控制元件篇-->Flex、Expanded
接下來我們說一下流式佈局
線上性佈局Row
中,如果子widget超出螢幕範圍,則會報溢位錯誤,如:
Row(
children: <Widget>[
Text("xxx"*100)
],
);
複製程式碼
執行效果:
那麼有人會說了,怎麼不用Text
元件中的maxLines
和overflow
呢?
很顯然,Row
元件的主軸是水平方向的,是讓其內容水平排布的,Row
預設只有一行,如果超出螢幕不會折行。這兩個屬性加上的話,是沒有效果的
但是如果把Row
換成Column
,那麼這兩個屬性就會起作用了
但是我們如果用上面兩個屬性來指定的話,這樣也不好,因為你並不確定有多少內容,所以我們想讓他如果超出螢幕顯示範圍的話,讓他自動折行,這種佈局稱之為流式佈局。
Flutter中通過Wrap
和Flow
來支援流式佈局
Wrap
流式佈局元件,如果內容寬度超出螢幕寬度,會自動折行顯示
原始碼示例
Wrap
的建構函式:
Wrap({
...
this.direction = Axis.horizontal,
this.alignment = WrapAlignment.start,
this.spacing = 0.0,
this.runAlignment = WrapAlignment.start,
this.runSpacing = 0.0,
this.crossAxisAlignment = WrapCrossAlignment.start,
this.textDirection,
this.verticalDirection = VerticalDirection.down,
List<Widget> children = const <Widget>[],
})
複製程式碼
屬性解釋
我們可以看到Wrap
的很多屬性在Row
、Column
和Flex
中也有,
所以重複的屬性就不說了,如果不知道的話,請移步:Flutter 佈局控制元件篇-->Row、Column
這裡說Wrap
特有的幾個屬性
spacing
主軸方向子元件的間距
相信學過HTML和CSS的話,應該不陌生
在這裡就是元件與元件之間的間隙
例如:
Wrap(
spacing: 20,
children: <Widget>[
Text("xxx"),
Text('yyy')
],
)
複製程式碼
spacing: 20,
表示的是xxx
與yyy
之間的距離
runSpacing
元件縱軸方向的間距,不做過多解釋
alignment
縱軸方向的對齊方式
例如:
Wrap(
spacing: 20, //主軸(水平)方向間距
runSpacing: 50, //縱軸方向間距
alignment:WrapAlignment.center , //沿主軸方向居中
children: <Widget>[
Text("xxx"*20),
Text('yyy')
],
)
複製程式碼
執行效果:
Flow
一般很少使用Flow
,因為太複雜了。Flow
主要用於一些需要自定義佈局策略或效能要求較高(如動畫中)的場景。
優點:
-
效能好;
Flow
是一個對子元件尺寸以及位置調整非常高效的控制元件,Flow
用轉換矩陣在對子元件進行位置調整的時候進行了優化:在Flow
定位過後,如果子元件的尺寸或者位置發生了變化,在FlowDelegate
中的paintChildren()
方法中呼叫context.paintChild
進行重繪,而context.paintChild
在重繪時使用了轉換矩陣,並沒有實際調整元件位置。 -
靈活;由於我們需要自己實現
FlowDelegate
的paintChildren()
方法,所以我們需要自己計算每一個元件的位置,因此,可以自定義佈局策略。
缺點:
-
使用複雜。
-
不能自適應子元件大小,必須通過指定父容器大小或實現
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,),
],
)
複製程式碼
實現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(有優化)
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;
}
}
複製程式碼
執行效果:
可以看到我們主要的任務就是實現paintChildren
,它的主要任務是確定每個子widget位置。由於Flow
不能自適應子widget的大小,我們通過在getSize
返回一個固定大小來指定Flow的大小。
傳送門
Flutter 佈局控制元件篇-->Align、Center
Flutter 佈局控制元件篇-->Stack、Positioned
Flutter 佈局控制元件篇-->Flex、Expanded