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

先来看效果

做这个功能的原因,因为日志比较长,内容很多,找起来非常不方便

只是简单的加粗标红的话,用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,
          ),
        ],
      );
  }
}
相关推荐
风华圆舞16 小时前
在 Flutter 鸿蒙项目里接入语音识别的完整思路
flutter·语音识别·harmonyos
风华圆舞18 小时前
鸿蒙 + Flutter 下如何让 HarmonyOS 能力真正服务于 AI 体验
人工智能·flutter·harmonyos
BreezeDove19 小时前
【Android】Flutter3.35项目启动超时问题
android·flutter
风华圆舞20 小时前
鸿蒙 MICROPHONE 权限在 Flutter 项目里怎么处理
flutter·华为·harmonyos
愚者Pro1 天前
切换本地 Flutter SDK 版本
flutter
TT_Close1 天前
别再复制旧 Flutter 工程了,真正拖慢你的不是业务代码
flutter·npm·visual studio code
风华圆舞1 天前
鸿蒙 + Flutter 下 AI 助手为什么要支持流式输出
人工智能·flutter·harmonyos
风华圆舞2 天前
鸿蒙 + Flutter 下 AI 页面的状态协同设计
人工智能·flutter·harmonyos
风华圆舞2 天前
鸿蒙语音播报功能 的 Flutter 侧封装思路
flutter·华为·harmonyos
brycegao3212 天前
Flutter 国际化富文本解决方案:基于双层占位符的轻量化图文混排方案
flutter·国际化·i18n·富文本·rtl·移动端工程架构