如何在 Flutter 中根据屏幕大小对长文本进行分页
Posted
技术标签:
【中文标题】如何在 Flutter 中根据屏幕大小对长文本进行分页【英文标题】:How to paginate long text based on screen size in Flutter 【发布时间】:2020-09-03 18:38:58 【问题描述】:我正在开发一款 Flutter 应用,用于向用户展示故事和文章。
目前,我正在 RichText 小部件中构建故事,以向文本的各种子字符串添加特殊样式和交互性,并且正在输入用于构建我正在填充 RichText 的 TextSpan 或 WidgetSpan 列表的数据来自一个自定义类的列表,该类包含实际的子字符串以及用于设置样式的信息。
为简单起见,假设此 List 的每个子项是:
CustomSpanClass(string: "substring that may include ,!'\" punctuation", weight: FontWeight.bold, ...etc)
目前,整个 RichText 都保存在一个可滚动的容器中,因此用户可以滚动浏览同一页面上的故事。我想给用户提供滑动阅读体验的选项,这就是我最大的障碍:输入的数据只是一个大的子字符串列表,所以我不知道什么时候文本会溢出屏幕。
有谁知道如何捕获哪个 TextSpan/WidgetSpan 会首先溢出由屏幕宽度和高度限制的容器的约束?我做了很多阅读,并认为也许我需要利用/访问 RenderObject,但我真的很茫然。
这有点过于简单了,但换句话说,如果我有一个 RichText:
Text.rich(
children: [
CustomSpan(string: "Flutter is pretty awesome,"),
CustomSpan(string: "and I love that it uses"),
CustomSpan(string: "widgets", weight: FontWeight.bold, color: Colors.blue),
CustomSpan(string: "and has hot reload capability."),
CustomSpan(string: "This span won't fit on the same screen for an iPhone 8 plus",),
CustomSpan(string: "And this span won't fully fit on the same screen for an iPhone 11 Pro."),
],
)
有谁知道我如何获取溢出页面的 Text.rich() 跨度的第一个子元素的索引,然后从那里开始构建第二个屏幕?
【问题讨论】:
【参考方案1】:您可以使用TextPainter重新实现基于this answer (android native)的文本分页算法。
要获取行 startOffset 和 endOffset 你可以使用 textPainter.getPositionForOffset https://***.com/a/56943995/2474384
我的尝试:
void _paginate()
final pageSize =
(_pageKey.currentContext.findRenderObject() as RenderBox).size;
_pageTexts.clear();
final textSpan = TextSpan(
text: widget.text,
style: widget.style,
);
final textPainter = TextPainter(
text: textSpan,
textDirection: TextDirection.ltr,
);
textPainter.layout(
minWidth: 0,
maxWidth: pageSize.width,
);
// https://medium.com/swlh/flutter-line-metrics-fd98ab180a64
List<LineMetrics> lines = textPainter.computeLineMetrics();
double currentPageBottom = pageSize.height;
int currentPageStartIndex = 0;
int currentPageEndIndex = 0;
for (int i = 0; i < lines.length; i++)
final line = lines[i];
final left = line.left;
final top = line.baseline - line.ascent;
final bottom = line.baseline + line.descent;
// Current line overflow page
if (currentPageBottom < bottom)
// https://***.com/questions/56943994/how-to-get-the-raw-text-from-a-flutter-textbox/56943995#56943995
currentPageEndIndex =
textPainter.getPositionForOffset(Offset(left, top)).offset;
final pageText =
widget.text.substring(currentPageStartIndex, currentPageEndIndex);
_pageTexts.add(pageText);
currentPageStartIndex = currentPageEndIndex;
currentPageBottom = top + pageSize.height;
final lastPageText = widget.text.substring(currentPageStartIndex);
_pageTexts.add(lastPageText);
Demo.
Full code.
【讨论】:
这真是太好了,不知道这是否可以应用于html渲染元素? @mohamnag IIRC,Flutter 团队在之前的 Flutter 更新中将 textPainter 函数移植到了 Flutter Web,所以我想我也会使用 Flutter Web HTML 渲染元素。您可以在 dartpad 上进行测试。 dartpad.dev/?id=36b249d1b5b5861a5ef58d958a50ad98以上是关于如何在 Flutter 中根据屏幕大小对长文本进行分页的主要内容,如果未能解决你的问题,请参考以下文章