Skip to content

在markdown_widget中实现长按高亮效果 #225

@iamouyang21

Description

@iamouyang21

您好,非常感谢您的付出,markdown_widget给我帮了很大的忙!!但是我需要在markdown_widget的基础上实现“高亮”的效果,如图:

Image

选中文字“Postpartum”后,点击菜单中的“高亮”按钮,期望将选中的文字增加黄色的背景(效果和前面的“during”文字相同)

目前我已经在简单场景中实现了这个功能,但是部分复杂的场景中还是有很多问题,我不知道我基于markdown_widget的实现是否是正确的方案,所以向您请教。

首先我使用SelectionArea搭配SelectionListener就能拿到选中的文案startOffset和endOffset,参考官网的demo实现:https://api.flutter.dev/flutter/widgets/SelectionListener-class.html

但是这里的startOffset和endOffset对应的位置是将md渲染后的位置,而不是md原文中的位置,所以我不能在md原文中直接使用startOffset和endOffset去匹配进行高亮渲染。

所以我需要在渲染时将startOffset和endOffset位置对应的文案找出来,然后进行高亮。

我观察到MarkdownWidget中的textGenerator会在每渲染一段文本就会触发里面的onAccepted方法一次,拿到文本,累积收集每个文本相对于全局的开始位置和结束位置。

然后我就可以知道选中的高亮文本的startOffset和endOffset是否在当前onAccepted触发时的文本范围内。

如果是在范围内,那么我就将当前文本进行切割,将高亮的部分切割出来。大概代码如下:

MarkdownWidget(
  data: _markdownText,
  selectable: false,
  markdownGenerator: MarkdownGenerator(
    textGenerator:
        (node, config, visitor) => CustomTextNode(
          //...省略
        ),
  ),
)
int offset = 0;

class CustomTextNode extends ElementNode {
  final String text;
  CustomTextNode()
  
  @override
  void onAccepted(SpanNode parent) {
    final textStyle = config.p.textStyle.merge(parentStyle);
    children.clear();
    startIndex = offset;
    offset += getTextLength();
    int endIndex = startIndex + getTextLength();

    // range为高亮选中的范围
    // 情况1:选中文本完全在当前文本块内
    if (range.startOffset >= startIndex && range.endOffset <= endIndex) {
        // 添加高亮前的普通文本
        if (range.startOffset > startIndex) {
          final beforeText = text.substring(
            startIndex - startIndex,
            range.startOffset - startIndex,
          );
          accept(TextNode(text: beforeText, style: textStyle));
        }

        // 添加高亮文本
        final highlightText = text.substring(
          range.startOffset - startIndex,
          range.endOffset - startIndex,
        );
        accept(
          // 这是一个高亮的TextSpan
          CustomWidgetNode(
            text: highlightText,
            style: highlightStyle,
            onTap: () => handleHighlightTap(highlightId),
          ),
        );
        // 添加高亮后的普通文本
        // ...省略
    }
  }
}

上面的方案可以实现我想要的高亮效果,但是有很多问题。比如如果是”公式”那么就不会触发textGenerator中的onAccepted方法,或者是html好像也不会触发,等等等各种情况。

想请教您我的方案是否可行,是否还有其他更佳的方案可以在渲染时去匹配高亮选中文案的startOffset和endOffse。

再次感谢您的付出!!!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions