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,
          ),
        ],
      );
  }
}
相关推荐
Python私教20 分钟前
Flutter主题最佳实践
前端·javascript·flutter
小蜜蜂嗡嗡12 小时前
【flutter列表播放器】
flutter
AiFlutter12 小时前
Flutter Web部署到子路径的打包指令
flutter
有趣的杰克12 小时前
Flutter InkWell组件去掉灰色遮罩
开发语言·javascript·flutter
Python私教12 小时前
Flutter动画容器
flutter
wills7771 天前
Flutter 状态管理框架Get
flutter·react native
阳仔_1001 天前
动态下发字体技术方案
flutter
Rudy10211 天前
分享我在flutter中使用的MVVM框架 - 2
前端·flutter
恋猫de小郭1 天前
什么?Flutter 又要凉了? Flock 是什么东西?
flutter
lqj_本人1 天前
<大厂实战场景> ~ Flutter&鸿蒙next 解析后端返回的 HTML 数据详解
flutter·华为·架构·harmonyos·1024程序员节