我有一个PageView
,我想用它来显示无限量的列中的小部件。我试图将小部件添加到一列中,直到溢出为止,然后从可能会溢出的小部件开始构建下一页。
到目前为止,这是我一起努力以使某些东西正常工作的目的:
class OverflowSliverChildDelegate extends SliverChildDelegate {
OverflowSliverChildDelegate({
@required this.itemBuilder,
}) : assert(itemBuilder != null),
super();
/// Used to build individual items to slot into pages.
final IndexedWidgetBuilder itemBuilder;
@override
Widget build(BuildContext context, int index) {
return LayoutBuilder(
builder: (context, constraints) {
// todo: handle this better
if (remainingHeight <= 0) throw Error();
var itemIndex = 0;
return PageView.builder(
itemBuilder: (context, pageIndex) {
final remainingHeight = constraints.maxHeight;
return Column(
// todo: move this generator to a separate method
children: () sync* {
// Build widgets until we run out of vertical space.
while (remainingHeight > 0) {
// todo: layout widget and get its height
final widget = itemBuilder(context, itemIndex++);
// remainingHeight -= widget.height
yield widget;
}
}(),
);
},
);
},
);
}
}
如果我只关心文本,则可以使用TextPainter
来布置文本并获得高度。有没有办法对任意小部件(可能有也可能没有自己的子代)执行此操作?
更新
查看了Offstage
小部件后,我尝试了一下,但是Flutter断言我没有从对象外部调用size
吸气剂。我也感到非常接近解决方案。
while (remainingHeight > 0) {
final widget = itemBuilder(context, itemIndex++);
final renderObj = ConstrainedBox(
child: widget,
constraints: constraints.copyWith(
maxHeight: remainingHeight,
),
).createRenderObject(context);
renderObj.layout(constraints);
remainingHeight -= renderObj.size.height;
if (remainingHeight > 0) {
yield widget;
}
}
答案 0 :(得分:0)
我最终研究了Flutter的银条列表源代码(实际上是ListView
小部件的后端),我能够弄清楚它是如何决定何时有足够的子级来填充视口和“缓存范围”。
我已经开始了一个新的Flutter项目,以尝试不同的方法,但是我对其进行了一些清理,并将其发布到pub.dev:https://pub.dev/packages/overflow_page_view
结果是PageView
,它为每个页面构建一个CustomScrollView
。我直接从Flutter的源代码中复制了RenderSliver
的代码,并更改了放置子代码的部分。理想情况下,我希望找到一种将自定义SliverConstraints
传递到ListView
或其他条子小部件的方法。
关于回答“如何在显示小部件之前检测小部件是否会超出其约束?”的问题:
我将RenderSliverList
的代码复制到我自己的类(NoCacheRenderSliverList
)中,并开始进行调整。应该注意的是,我使用NeverScrollableScrollPhysics
是为了不必担心滚动(因为我不需要它)。
首先,在performLayout
方法的顶部,我将targetEndScrollOffset
更改为剩余的绘画范围,与滚动偏移量或缓存范围无关。这里只关注可见区域:
final double targetEndScrollOffset = this.constraints.remainingPaintExtent;
接下来,我需要更改嵌套的bool advance()
方法,以在子项的底部超出视口边缘时返回false:
assert(child != null);
final SliverMultiBoxAdaptorParentData childParentData =
child.parentData as SliverMultiBoxAdaptorParentData;
childParentData.layoutOffset = endScrollOffset;
assert(childParentData.index == index);
// In the original source, this is assigned directly to endScrollOffset
final tmp = childScrollOffset(child) + paintExtentOf(child);
if (tmp > targetEndScrollOffset) {
return false;
}
endScrollOffset = tmp;
return true;
使用上面的代码,我们仍然将最终显示一个延伸到视口之外的子项。我的最后一个更改是确保该孩子被垃圾回收:
// Finally count up all the remaining children and label them as garbage.
if (child != null) {
// By commenting out this line, we ensure the final child is garbage collected.
// child = childAfter(child);
while (child != null) {
trailingGarbage += 1;
child = childAfter(child);
}
}
仅此而已。如果有人有任何改进或需要澄清的地方,我很乐意对此进行更新。