先来看效果
做这个功能的原因,因为日志比较长,内容很多,找起来非常不方便
只是简单的加粗标红的话,用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,
),
],
);
}
}