一、需求来源
工作中经常遇到一些富文本的样式,每次写许多的 TextSpan 的不胜其烦;今天终于想到一种完美处理高亮文本样式和段落样式的办法,核心是通过 splitMapJoin 方法配合 RegExp,代码极其精简,使用极度舒适。
二、使用对比
1、原始代码
Text.rich(
TextSpan(
children: [
TextSpan(
text: "普通文本1",
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Color(0xFF1A1A1A),
),
),
TextSpan(
text: "高亮1",
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Color(0xFF737373),
),
),
TextSpan(
text: "普通文本2",
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Color(0xFF737373),
),
),
TextSpan(
text: "高亮2",
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Color(0xFF737373),
),
),
TextSpan(
text: "普通文本3",
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Color(0xFF737373),
),
),
],
),
),
2、封装后代码
final text = "普通文本1高亮1普通文本2高亮2普通文本3高亮3普通文本4";
final textTaps = ["高亮1", "高亮2", "高亮3"];
...
Text.rich(
TextSpan(
children: RichTextExt.createTextSpans(
text: text,
textTaps: textTaps,
// linkStyle: TextStyle(fontSize: 18.0, color: Colors.red),
onLink: (textTap) {
ddlog(textTap);
},
),
),
),
三、实现源码(35 行)
extension RichTextExt on RichText {
/// 创建 List<TextSpan>
///
/// text 整个段落
/// textTaps 高亮字符串数组
/// style 段落样式
/// linkStyle 高亮样式
/// prefix 切割符号,避免和文章包含字符串重复
/// suffix 切割符号,避免和文章包含字符串重复
/// onLink 高亮部分点击事件
static List<TextSpan> createTextSpans({
required String text,
required List<String> textTaps,
TextStyle? style,
TextStyle? linkStyle,
String prefix = "_&t",
String suffix = "_&t",
required void Function(String textTap) onLink,
}) {
final pattern = textTaps.map((d) => RegExp.escape(d)).join('|');
final regExp = RegExp(pattern, multiLine: true);
final textNew = text.splitMapJoin(regExp,
onMatch: (m) => '$prefix${m[0]}$suffix', // (or no onMatch at all)
onNonMatch: (n) => n,
);
final list = textNew.split(RegExp('$prefix|$suffix'));
return list.map((e) {
if (e.isNotEmpty) {
final isEquel = textTaps.contains(e);
if (isEquel) {
return TextSpan(
text: e,
style: linkStyle ?? TextStyle(color: Colors.blue),
recognizer: TapGestureRecognizer()
..onTap = () {
onLink(e);
},
);
}
}
return TextSpan(text: e, style: style);
}).toList();
}
}
四、总结
1、目前没有找比这更加优雅的,同时支持高亮文本点击事件的代码实现,完全是灵光一闪的杰作。(如果有,请一定联系我!!!)
2、onLink 是高亮文本点击回调,推荐开始用一个Map保存高亮文字和对应的链接;例如:
var linkMap = {
'《用户协议》': 'https://flutter.dev',
'《隐私政策》': 'https://flutter.dev',
};
Text.rich(
TextSpan(
children: RichTextExt.createTextSpans(
text: text,
textTaps: linkMap.keys.toList(),
// linkStyle: TextStyle(fontSize: 18.0, color: Colors.red),
onLink: (textTap) {
ddlog(textTap);// linkMap[textTap]即为相对应的协议链接
},
),
),
),