【问题标题】:How to paginate long text based on screen size in Flutter如何在 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() 跨度的第一个子元素的索引,然后从那里开始构建第二个屏幕?

【问题讨论】:

    标签: flutter dart pagination richtext


    【解决方案1】:

    您可以使用TextPainter重新实现基于this answer (Android native)的文本分页算法。

    要获取行 startOffset 和 endOffset 你可以使用 textPainter.getPositionForOffset https://stackoverflow.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://stackoverflow.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
    猜你喜欢
    • 2021-07-26
    • 1970-01-01
    • 1970-01-01
    • 2021-10-11
    • 2014-09-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多