flutter - 如何在渲染之前检测小部件是否会溢出其约束?
问题描述
我有一个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;
}
}(),
);
},
);
},
);
}
}
如果我只关心文本,我可以使用 aTextPainter
来布局文本并获取高度。有没有办法为任意小部件(可能有也可能没有自己的孩子)做到这一点?
更新
在查看了Offstage
小部件之后,我尝试了这个,但 Flutter 断言我不会size
从对象外部调用 getter。我也觉得很接近解决方案。
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;
}
}
解决方案
最后,我深入研究了 Flutter 的 sliver 列表源代码(本质上是ListView
小部件的后端),我能够弄清楚它是如何决定何时有足够的孩子来填充视口和“缓存范围”的。
我已经开始了一个新的 Flutter 项目以尝试不同的方法,但我已经对其进行了一些清理并将其发布到pub.dev:https://pub.dev/packages/overflow_page_view
结果是为每个页面PageView
构建一个。CustomScrollView
我直接RenderSliver
从 Flutter 源代码中复制了这些东西的代码,并更改了它放置子元素的部分。理想情况下,我希望找到一种将自定义传递SliverConstraints
给ListView
或其他 sliver 小部件的方法。
至于回答“如何检测小部件在渲染之前是否会溢出其约束?”的问题:
我将 for 的代码复制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);
}
}
这就是它的全部。如果有人有任何改进或需要澄清,我很乐意更新。
推荐阅读
- ruby - Ruby 解析无效的 JSON
- json - 如何在 PostgreSQL 中解析这个 JSON 字段?
- r - R在使用带有xlim的基本图时如何自动调整y轴
- javascript - 变量和 if 语句在 Javascript 中产生错误
- java - 在带有 appengine-maven-plugin 的 appengine 上的多模块 maven java 项目中部署期间跳过某些模块
- javascript - 如何获取特定列标题过滤器的 dataFiltered-Callback?
- r - RStudio 警告消息:(来自记忆):as_tibble 正在改变..请使用
- javascript - 单击向下/向上键在 ul 元素内滚动 - React
- api - 如何在正文请求中将用户凭据传递给被邀请人而不是作为查询参数
- java - 在 Spring Boot 应用程序中使用休眠向现有表添加外键关系