onTextChanged method
Implementation
void onTextChanged(String newText, BuildContext context) {
String oldText = lastChangedText;
// 检测删除操作
if (newText.length < oldText.length) {
int diffIndex = 0;
while (diffIndex < newText.length &&
diffIndex < oldText.length &&
newText[diffIndex] == oldText[diffIndex]) {
diffIndex++;
}
// 修改正则,匹配 "@姓名 ",支持中文、字母、数字与下划线
RegExp mentionRegExp = RegExp(r'@[\w\u4e00-\u9fa5]+ ');
for (var m in mentionRegExp.allMatches(oldText)) {
if (diffIndex >= m.start && diffIndex < m.end) {
// 计算新的结束索引,避免越界
int endIndex = m.end > newText.length ? newText.length : m.end;
newText = newText.substring(0, m.start) + newText.substring(endIndex);
controller.value = controller.value.copyWith(
text: newText,
selection: TextSelection.collapsed(offset: m.start));
String mentionName = oldText.substring(m.start, m.end);
removeUserAtInfo(mentionName);
break;
}
}
}
RCKChatProvider chatProvider = context.read<RCKChatProvider>();
if (newText.isEmpty) {
chatProvider.clearDraft();
} else {
chatProvider.saveDraft(newText);
}
// 当文本增加字符且新增加的字符中包含 '@' 时触发
if (newText.length > oldText.length &&
chatProvider.conversation.conversationType ==
RCIMIWConversationType.group) {
int cursorIndex = controller.selection.baseOffset;
// 验证 cursorIndex 的有效性
if (cursorIndex == -1) {
cursorIndex = newText.length;
}
if (cursorIndex > 0 && newText[cursorIndex - 1] == '@') {
context.read<RCKAudioPlayerProvider>().stopVoiceMessage();
context.read<RCKVoiceRecordProvider>().cancelRecord();
Navigator.pushNamed(context, '/at_people',
arguments: {'groupId': chatProvider.conversation.targetId})
.then((value) {
RCKUserAtInfo? atInfo = value as RCKUserAtInfo?;
if (atInfo == null) return;
final currentText = controller.text;
final selection = controller.selection;
int cursorIndex = selection.baseOffset;
// 验证 cursorIndex 的有效性
if (cursorIndex == -1) {
cursorIndex = currentText.length;
}
// 判断光标前一个字符是否为 '@'
if (cursorIndex > 0 && currentText[cursorIndex - 1] == '@') {
final newText = currentText.replaceRange(
cursorIndex,
cursorIndex,
'${atInfo.name} ',
);
controller.value = controller.value.copyWith(
text: newText,
selection: TextSelection.collapsed(
offset: cursorIndex + atInfo.name.length + 1,
),
);
}
addUserAtInfo(atInfo.userId);
if (context.mounted) {
onTextChanged(controller.text, context);
}
});
}
}
lastChangedText = newText;
}