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,
          ),
        ],
      );
  }
}
相关推荐
Georgewu9 天前
【HarmonyOS 5】鸿蒙跨平台开发方案详解(一)
flutter·harmonyos
爱吃鱼的锅包肉9 天前
Flutter开发中记录一个非常好用的图片缓存清理的插件
flutter
张风捷特烈10 天前
每日一题 Flutter#13 | build 回调的 BuildContext 是什么
android·flutter·面试
恋猫de小郭10 天前
Flutter 又双叒叕可以在 iOS 26 的真机上 hotload 运行了,来看看又是什么黑科技
android·前端·flutter
QC七哥10 天前
跨平台开发flutter初体验
android·flutter·安卓·桌面开发
小喷友10 天前
Flutter 从入门到精通(水)
前端·flutter·app
恋猫de小郭10 天前
Flutter 里的像素对齐问题,深入理解为什么界面有时候会出现诡异的细线?
android·前端·flutter
tbit11 天前
dart私有命名构造函数的作用与使用场景
flutter·dart
法的空间11 天前
JsonToDart,你已经是一个成熟的工具了,接下来就靠你自己继续进化了!
android·flutter·ios