import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:tuple/tuple.dart';
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",
void Function(String textTap)? onLink,
}) {
final pattern = textTaps.map((d) => RegExp.escape(d)).join('|');
final regExp = RegExp(pattern, multiLine: true, caseSensitive: false);
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) ||
textTaps.contains(e.toLowerCase()) ||
textTaps.contains(e.toUpperCase());
if (isEquel) {
return TextSpan(
text: e,
style: linkStyle ?? TextStyle(color: Colors.blue),
recognizer: onLink == null ? null : TapGestureRecognizer()
?..onTap = () {
onLink?.call(e);
},
);
}
}
return TextSpan(text: e, style: style);
}).toList();
}
}
extension TextSpanExt on TextSpan {
/// 二次赋值
TextSpan copyWith({
String? text,
List<InlineSpan>? children,
TextStyle? style,
GestureRecognizer? recognizer,
PointerEnterEventListener? onEnter,
PointerExitEventListener? onExit,
String? semanticsLabel,
Locale? locale,
bool? spellOut,
ValueChanged<String?>? onLink,
}) {
final content = text ?? this.text;
TapGestureRecognizer? gesture;
if (onLink != null) {
gesture = TapGestureRecognizer()..onTap = () => onLink.call(content);
}
return TextSpan(
text: content,
children: children ?? this.children,
style: style ?? this.style,
recognizer: gesture ?? recognizer ?? this.recognizer,
onEnter: onEnter ?? this.onEnter,
onExit: onExit ?? this.onExit,
semanticsLabel: semanticsLabel ?? this.semanticsLabel,
locale: locale ?? this.locale,
spellOut: spellOut ?? this.spellOut,
);
}
}
知识点:
1 RegExp.escape(d) 这个函数的主要作用是用于转义正则表达式特殊字符。比如
/// print(RegExp.escape('dash@example.com')); // dash@example.com
/// print(RegExp.escape('a+b')); // a+b
/// print(RegExp.escape('a*b')); // a*b
/// print(RegExp.escape('{a-b}')); // {a-b}
/// print(RegExp.escape('a?')); // a?
2 RegExp(pattern, multiLine: true, caseSensitive: false); multiLine 多行匹配 caseSensitive 是否忽略大小写
3 splitMapJoin
final textNew = text.splitMapJoin(
regExp,
//匹配到的内容替换成返回值
onMatch: (m) => '$prefix${m[0]}$suffix', // (or no onMatch at all)
//未匹配到的返回原本的值
onNonMatch: (n) => n,
);