CustomMultiChildLayout允许我们通过delegate自定义子组件的布局约束、位置以及父组件的大小(父组件大小不依赖于子组件的情况下),和CustomSingleChildLayout基本一样,区别就是CustomSingleChildLayout包裹一个子控件,而CustomMultiChildLayout包裹多个。
接下来我会用CustomMultiChildLayout 控件实现一个图片瀑布流墙的效果,效果如下:
图片瀑布流 img
CustomMultiChildLayout控件需要两个重要参数
一、delegate 具体处理子控件的代理
二、children 子控件集合
子控件必须被LayoutId 控件包裹起来,因为这样才可以找到它。
创建子控件
final items = List.generate(20, (index) {
int ys = index % 8+1;
return LayoutId(
id: "id_$index",
child: Container(
width: 100,
height: 40 + Random().nextDouble() * 100,
color: Colors.red[100*ys],
child: FlutterLogo(),//替换成你的图片控件
));
});
为了看到效果我们这里把高度和颜色都弄成随机, id 则是子控件唯一标识。
然后使用控件:
CustomMultiChildLayout(
delegate: ProxyClass(items.length, 3, 5),
children: items,
)
这里ProxyClass 是实现效果的具体方法:
class ProxyClass extends MultiChildLayoutDelegate {
int num;
int columnNum;
int padding;
ProxyClass(this.num, this.columnNum, this.padding);
@override
void performLayout(Size size) {
double lastWidth = size.width - columnNum * padding;
double itemWidth = lastWidth / columnNum;
double offsetX = 0;
List columnH = List.generate(columnNum, (index) {
return 0.0;
});
for (int i = 0; i < num; i++) {
int ys = i % columnNum;
print("$ys === ${columnH[ys]}");
Size itemSize = layoutChild(
"id_$i",
BoxConstraints(
minWidth: itemWidth,
maxWidth: itemWidth,
minHeight: 0,
maxHeight: 1000));
positionChild("id_$i", Offset(offsetX+padding*0.5, columnH[ys]));
offsetX += padding + itemWidth;
if (offsetX >= size.width - 1) {
offsetX = 0;
}
columnH[ys] += itemSize.height + padding;
print("size : ${columnH[ys]}----$offsetX");
}
}
@override
bool shouldRelayout(covariant MultiChildLayoutDelegate oldDelegate) {
return true;
}
}
主要通过id查找每个具体的子控件,columnNum是竖直方向一共多少列,padding是每个模块的间隙。
shouldRelayout是告诉系统每次刷新是是否重绘,我们这里写的是每次都需要重绘。
performLayout 则是具体布局实现方法,size是 自定义控件的具体大小。
layoutChild 通过给子控件设置约束来获取子控件大小。
positionChild 是布局子控件位置的方法。
上面逻辑主要是通过计算每个子控件的offset来排版成瀑布流的样子。
如果对你有帮助记得点赞哦 !!