我正在尝试构建一个自定义版本的列(称为 CustomColumn),它可以惰性地布置子项,类似于 ListView 的功能,但可以更好地控制它们的布置方式。我所坚持的最基本的一点是:如何在 CustomColumn 的 PerformLayout 阶段创建一个小部件。我正在努力寻找一个关于如何做到这一点的最小示例。我能找到的唯一一个能做类似事情的小部件是 ListView,但我无法创建它的最小版本。
在布局期间,我尝试检查可用空间,并且仅在需要时创建一个子项(使用 IndexedWidgetBuilder)并将其布局。我认为应该有像“adoptChild”或“discardChild”这样的功能来管理子小部件的生命周期,但我找不到正确的 API。
我对滚动、调试溢出绘画或布局期间动态(或惰性)小部件创建之外的任何内容(目前)特别不感兴趣。实现此行为所需的最小类集是什么?
如果不看代码,就很难回答确切的解决方案。 我认为您需要创建 2 个小部件,首先命名为 CustomColumn,然后您需要 LayoutWidget。类似的事情。
class CustomColumn extends StatefulWidget {
final IndexedWidgetBuilder itemBuilder;
final int itemCount;
CustomColumn({required this.itemBuilder, required this.itemCount});
@override
_CustomColumnState createState() => _CustomColumnState();
}
class _CustomColumnState extends State<CustomColumn> {
@override
Widget build(BuildContext context) {
return CustomMultiChildLayout(
delegate: _CustomColumnLayoutDelegate(widget.itemCount, widget.itemBuilder),
children: List.generate(widget.itemCount, (index) {
return LayoutId(
id: index,
child: Container(),
);
}),
);
}
}
之后你需要MultiChildLayout
class _CustomColumnLayoutDelegate extends MultiChildLayoutDelegate {
final int itemCount;
final IndexedWidgetBuilder itemBuilder;
_CustomColumnLayoutDelegate(this.itemCount, this.itemBuilder);
@override
void performLayout(Size size) {
double yOffset = 0.0;
for (int i = 0; i < itemCount; i++) {
// Create the child widget lazily
final child = itemBuilder(context, i);
if (child is! SizedBox) {
// Wrap the child in a SizedBox to get its size
final childSize = child is RenderBox ? child.size : Size.zero;
if (yOffset + childSize.height <= size.height) {
// Layout the child if there's enough space
layoutChild(i, BoxConstraints.tightFor(width: size.width, height: childSize.height));
positionChild(i, Offset(0, yOffset));
yOffset += childSize.height;
} else {
// Break if there's no more space
break;
}
}
}
}
@override
bool shouldRelayout(covariant MultiChildLayoutDelegate oldDelegate) {
return true; // Always relayout for simplicity
}
}
并像这样在任何地方使用它
CustomColumn(
itemCount: 100,
itemBuilder: (context, index) {
return Container(
height: 50,
color: index.isEven ? Colors.blue : Colors.green,
child: Center(child: Text('Item $index')),
);
},
),
)
我已经为这种功能开发了一个基本包。你可以查看这个包的文档或github,这个包通过使用包中的SpacedColumn和SpacedRow小部件来维护列和行之间的空间。
https://pub.dev/packages/equal_space