flutter 一段长文本实现检索功能,检索的文本加粗标红

发布于:2024-10-10 ⋅ 阅读:(18) ⋅ 点赞:(0)

先来看效果
在这里插入图片描述
做这个功能的原因,因为日志比较长,内容很多,找起来非常不方便
只是简单的加粗标红的话,用TextSpan自己也可以做,主要日志还涉及选择复制,涉及的东西很多,想到了 extended_text,偷师了一下。
extended_text 并不能直接拿来使用,需要添加specialTextSpanBuilder来筛选搜索的关键词。

CommonSelectionArea(
                      child:
                      ExtendedText(
                        log,
                        softWrap: true,
                        style: const AppTextStyleData(
                            color: AppColor.black,
                            fontSize: 14,
                            fontWeight: FontWeight.w400,
                            height: 1.5)
                            .ts,
                        specialTextSpanBuilder: (flag??'').isEmpty? null:MySpecialTextSpanBuilder(flag??''),
                      ).makeConstraints((make) {
                        make.top.bottom.equalTo(4);
                      }),
                    );

CommonSelectionArea 用的是SelectionArea 系统组件,SelectionArea提供适应不同平台的选择控件。用于在特定屏幕上启用选择功能。selectionControls 可以自定义选择控件。

class CommonSelectionArea extends StatelessWidget {
  const CommonSelectionArea({
    super.key,
    required this.child,
    this.joinZeroWidthSpace = false,
  });
  final Widget child;
  final bool joinZeroWidthSpace;

  @override
  Widget build(BuildContext context) {
    SelectedContent? selectedContent;
    return SelectionArea(
      selectionControls: MyTextSelectionControls(),
      contextMenuBuilder:
          (BuildContext context, SelectableRegionState selectableRegionState) {
        return AdaptiveTextSelectionToolbar.buttonItems(
          buttonItems: <ContextMenuButtonItem>[
            ContextMenuButtonItem(
              onPressed: () {
                selectableRegionState
                    .copySelection(SelectionChangedCause.toolbar);

                // remove zeroWidthSpace
                if (joinZeroWidthSpace) {
                  Clipboard.getData('text/plain').then((ClipboardData? value) {
                    if (value != null) {
                      // remove zeroWidthSpace
                      final String? plainText = value.text?.replaceAll(
                          ExtendedTextLibraryUtils.zeroWidthSpace, '');
                      if (plainText != null) {
                        Clipboard.setData(ClipboardData(text: plainText));
                      }
                    }
                  });
                }
              },
              type: ContextMenuButtonType.copy,
            ),
            ContextMenuButtonItem(
              onPressed: () {
                selectableRegionState.selectAll(SelectionChangedCause.toolbar);
              },
              type: ContextMenuButtonType.selectAll,
            ),
            // ContextMenuButtonItem(
            //   onPressed: () {
            //     launchUrl(Uri.parse(
            //         'mailto:xxx@live.com?subject=extended_text_share&body=${selectedContent?.plainText}'));
            //     selectableRegionState.hideToolbar();
            //   },
            //   type: ContextMenuButtonType.custom,
            //   label: 'like',
            // ),
          ],
          anchors: selectableRegionState.contextMenuAnchors,
        );
      },
      onSelectionChanged: (SelectedContent? value) {
        print(value?.plainText);
        selectedContent = value;
      },
      child: child,
    );
  }
}

MySpecialTextSpanBuilder 继承SpecialTextSpanBuilder,重写createSpecialText 方法。

class MySpecialTextSpanBuilder extends SpecialTextSpanBuilder {
  MySpecialTextSpanBuilder(
       this.searchFlag
      );
  String  searchFlag;
  @override
  SpecialText? createSpecialText(String flag,
      {TextStyle? textStyle,
      SpecialTextGestureTapCallback? onTap,
      int? index}) {
    if (searchFlag == ''||searchFlag.isEmpty) {
      return null;
    }
    if (flag == ''||flag.isEmpty) {
      return null;
    }

    // index is end index of start flag, so text start index should be index-(flag.length-1)
    if (isStart(flag,searchFlag)) {
      return SelectText(
        textStyle,
        onTap,
        searchFlag,
        start: index!- (searchFlag.length - 1) ,
      );
    }
    return null;
  }
}

SelectText 为关键词的TextSpan 呈现。

class SelectText extends SpecialText {
  SelectText(TextStyle? textStyle, SpecialTextGestureTapCallback? onTap,this.searchFlag,
      {required this.start})
      : super(searchFlag,'', textStyle, onTap: onTap);
  final int start;
  String searchFlag;
  @override
  InlineSpan finishText() {
    final TextStyle? textStyle = (this.textStyle ?? const TextStyle())
        .copyWith(color: Colors.red, fontSize: 14.0,fontWeight: FontWeight.w600);
    final TextStyle? textStyle2 = (this.textStyle ?? const TextStyle())
        .copyWith(color: Colors.black, fontSize: 14.0);
    // final String atText = toString();
    // print("--------startFlag:$startFlag---getContent:${getContent()}---endFlag:$endFlag");

    return TextSpan(
        text: startFlag,
        style: textStyle,
        children: <InlineSpan>[
          TextSpan(
            text: getContent(),
            style:textStyle2,
          ),
        ],
      );
  }
}