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,
          ),
        ],
      );
  }
}
相关推荐
旭日猎鹰25 分钟前
Flutter踩坑记录(三)-- 更改入口执行文件
flutter
旭日猎鹰25 分钟前
Flutter踩坑记录(一)debug运行生成的项目,不能手动点击运行
flutter
️ 邪神25 分钟前
【Android、IOS、Flutter、鸿蒙、ReactNative 】自定义View
flutter·ios·鸿蒙·reactnative·anroid
比格丽巴格丽抱12 小时前
flutter项目苹果编译运行打包上线
flutter·ios
SoaringHeart12 小时前
Flutter进阶:基于 MLKit 的 OCR 文字识别
前端·flutter
AiFlutter16 小时前
Flutter通过 Coap发送组播
flutter
嘟嘟叽2 天前
初学 flutter 环境变量配置
flutter
iFlyCai2 天前
深入理解Flutter生命周期函数之StatefulWidget(一)
flutter·生命周期·dart·statefulwidget
sunly_2 天前
Flutter:photo_view图片预览功能
android·javascript·flutter
Summer不秃2 天前
Flutter中sqflite的使用案例
flutter