13 CustomMultiChildLayout-2
childSize
相信大家都能故名思义,那 childId
是什么呢?
这就要从 MultiChildLayoutDelegate
的实现说起,在 MultiChildLayoutDelegate
内部会有一个 Map _idToChild;
对象,这个 Map
对象保存着 Object id
和 RenderBox
的映射关系,而在 MultiChildLayoutDelegate
中获取 RenderBox
都需要通过 id
获取。
_idToChild
这个 Map
是在 RenderBox performLayout
时,在 delegate._callPerformLayout
方法内创建的,创建后所用的 id
为 MultiChildLayoutParentData
中的 id, 而 MultiChildLayoutParentData
的 id ,可以通过 LayoutId
嵌套时自定义指定赋值。
而完成上述布局,我们需要知道每个 child 的 index ,所以我们可以把 index 作为 id 设置给每个 child 的 LayoutId
。
所以我们可以通过 LayoutId
指定 id 为数字 index , 同时告知 delegate ,这样我们就知道 child 顺序和位置啦。
这个 id 是
Object
类型 ,所以你懂得,你可以赋予很多属性进去。
如下代码所示,这样在自定义的 CircleLayoutDelegate
中,就知道每个控件的 index
位置,也就是知道了,圆形布局中每个 item 需要的位置。
我们只需要通过 index
,计算出 child 所在的角度,然后利用 layoutChild
和 positionChild
对每个item进行布局即可,完整代码:GSYFlutterDemo
///自定义实现圆形布局
class CircleLayoutDelegate extends MultiChildLayoutDelegate {
final List<String> customLayoutId;
final Offset center;
Size childSize;
CircleLayoutDelegate(this.customLayoutId,
{this.center = Offset.zero, this.childSize});
@override
void performLayout(Size size) {
for (var item in customLayoutId) {
if (hasChild(item)) {
double r = 100;
int index = int.parse(item);
double step = 360 / customLayoutId.length;
double hd = (2 * math.pi / 360) * step * index;
var x = center.dx + math.sin(hd) * r;
var y = center.dy - math.cos(hd) * r;
childSize ??= Size(size.width / customLayoutId.length,
size.height / customLayoutId.length);
///设置 child 大小
layoutChild(item, BoxConstraints.loose(childSize));
final double centerX = childSize.width / 2.0;
final double centerY = childSize.height / 2.0;
var result = new Offset(x - centerX, y - centerY);
///设置 child 位置
positionChild(item, result);
}
}
}
@override
bool shouldRelayout(MultiChildLayoutDelegate oldDelegate) => false;
}
总的来说,第二种实现方式相对简单,但是也丧失了一定的灵活性,可自定义控制程度更低,但是也更加规范与间接,同时我们自己实现 RenderBox
时,也可以用类似的 delegate 的方式做二次封装,这样的自定义布局会更行规范可控。
学员评价