先来看效果
做这个功能的原因,因为日志比较长,内容很多,找起来非常不方便
只是简单的加粗标红的话,用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;@overrideWidget 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 zeroWidthSpaceif (joinZeroWidthSpace) {Clipboard.getData('text/plain').then((ClipboardData? value) {if (value != null) {// remove zeroWidthSpacefinal 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;@overrideSpecialText? 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;@overrideInlineSpan 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,),],);}
}