
流式布局很經(jīng)典了,F(xiàn)lutter 提供了2種方式,Wrap 用著省事,weightTree 里配置就行了,F(xiàn)low 可以自定義顯示,需要自己寫代碼
Wrap
Wrap 好寫,子 view 寫死,不知道有沒有可以代碼動態(tài)添加的實現(xiàn)方式。Wrap 和 Flow 其實和 row、colume 產(chǎn)不多,就是多了可以自動換行的選項,屬性的話大家參照 row、colume 理解
先看屬性:
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>[],
})
-
direction- 這個是方向,一般我們都是用在橫向的多 -
alignment- 主軸對齊方式,參考 row、colume 的 -
spacing- 主軸方向每個 weight 間隔 -
runSpacing- 交叉軸方向間隔,在橫向時這玩意就是行間距 -
runAlignment、crossAxisAlignment- 我試了試沒啥用,有alignment就夠用了
class AText extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
alignment: Alignment.center,
width: 100,
height: 40,
color: Colors.blueAccent,
child: Text("AAAAAA"),
);
}
}
class AA extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Wrap(
spacing: 25,
runSpacing: 5,
direction: Axis.horizontal,
alignment: WrapAlignment.spaceEvenly,
children: <Widget>[
AText(),
AText(),
AText(),
AText(),
AText(),
AText(),
AText(),
],
);
}
}

Wrap 使用上很方便,一般能用 Wrap 就用 Wrap,雖說 Wrap 能實現(xiàn)的 Flow 也一樣 OK,但是 Flow 需要自己寫代碼實現(xiàn)換行
Flow
我們一般很少會使用Flow,因為其過于復(fù)雜,需要自己實現(xiàn)子widget的位置轉(zhuǎn)換,在很多場景下首先要考慮的是Wrap是否滿足需求。Flow主要用于一些需要自定義布局策略或性能要求較高(如動畫中)的場景
其實我對 Flow 并不熟悉,據(jù)說 Flow 性能比 Wrap 要好,具體的我也沒看懂,下面是原話:
性能好;Flow是一個對child尺寸以及位置調(diào)整非常高效的控件,F(xiàn)low用轉(zhuǎn)換矩陣(transformation matrices)在對child進行位置調(diào)整的時候進行了優(yōu)化:在Flow定位過后,如果child的尺寸或者位置發(fā)生了變化,在FlowDelegate中的paintChildren()方法中調(diào)用context.paintChild 進行重繪,而context.paintChild在重繪時使用了轉(zhuǎn)換矩陣(transformation matrices),并沒有實際調(diào)整Widget位置
flow 抽象了一個接口 FlowDelegate 用來定位子 view 的位置,F(xiàn)lowDelegate 接口里最重要的就是 paintChildren()方法了,在這個方法里我們遍歷所有子 view 計算他們的位置,然后根據(jù)自己的意愿換行還是換列,能自定義效果
但是 flow 非常蛋疼的地方在于無法支持子 view 自定義大小,所有子 view 必須統(tǒng)一設(shè)置寬高值,這在實際中怎么可能啊,getSize 方法返回子view 寬高大小

這里我們給 FlowDelegate 接口傳入橫向間距和縱向間距
class AA extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Flow(
// 這里我們給 FlowDelegate 接口傳入橫向間距和縱向間距
delegate: ADelegate(10, 10),
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.black,),
new Container(width: 80.0, height:80.0, color: Colors.purple,),
],
);
}
}
FlowDelegate 接口不是照搬網(wǎng)上的,是我自己寫的,看著還算工整,網(wǎng)上的代碼風(fēng)格太亂,沒法看
class ADelegate extends FlowDelegate {
var wSpacing = 0.0;
var hSpacing = 0.0;
ADelegate(this.wSpacing, this.hSpacing);
@override
void paintChildren(FlowPaintingContext context) {
var pWidth = context.size.width;
var x = 0.0;
var y = 0.0;
var childWidth = 0.0;
var childSize = new Size(0, 0);
for (int i = 0; i < context.childCount; i++) {
childSize = context.getChildSize(i);
childWidth = x + childSize.width;
// 子 view 的寬度要是比父控件的 width 還大就得換行了
if (childWidth <= pWidth) {
// 不換行,定位子 view 位置
context.paintChild(I,
transform: new Matrix4.translationValues(x, y, 0.0));
// 然后把 x 坐標(biāo)偏移量算出來
x = childWidth + wSpacing;
} else {
// 此時換行了,先重置 x 坐標(biāo)偏移量
x = 0;
// 因為要顯示在當(dāng)前行的下一行,就得先把下一行起始 Y 坐標(biāo)算出來
y = y + childSize.height + hSpacing;
context.paintChild(I,
transform: new Matrix4.translationValues(x, y, 0.0));
// 然后別忘了計算 x 坐標(biāo)偏移量
x = childSize.width + wSpacing;
}
}
}
getSize(BoxConstraints constraints) {
//指定Flow的大小
return Size(double.infinity, 200.0);
}
@override
bool shouldRepaint(FlowDelegate oldDelegate) {
return oldDelegate != this;
}
}