如何在文本框输入@符号时显示与隐藏提及组件?
社交应用提及功能实现问题解决
问题背景
我通过文本框的onChanged属性监听内容变化,想要实现社交应用的提及功能:输入@时显示提及组件,输入空格后隐藏;同时需要支持在文本中间输入@时也能触发组件。
期望实现效果
- 检测到
@符号输入时显示提及组件,输入空格则隐藏; - 在文本中间输入
@时也能触发显示提及组件。
已尝试方案的问题
使用正则匹配含@的内容,通过分割文本判断是否显示组件,但效果不符合预期——在文本框内容中间输入@时,提及组件不会触发。
原代码
void onChanged(String value) { final _mentionRegEx = RegExp(AppUtils.mentionPattern, caseSensitive: false); final _checkRegEx = RegExp( r"^@[a-zA-Z0-9_ ]+[a-zA-Z0-9_]*$", // caseSensitive: false, multiLine: true, ); Iterable<Match> matches = _mentionRegEx.allMatches(value); for (var m in matches) { String match = m[0]!; final withoutMentonTag = match.substring(1); state = state.copyWith(matched: withoutMentonTag); print('MENTION => ${state.matched}'); } final sentences = value.split(' '); for (var sentence in sentences) { print('SENTENCE => $sentence'); final words = sentence.split(' '); final withMentionTag = words.last; if (_checkRegEx.hasMatch(sentence)) { toggleMentionWidget(true); String withoutMentionTag = withMentionTag.substring(1); print('MENTIONED USER => $withoutMentionTag'); } else { toggleMentionWidget(false); } } }
问题分析与修正方案
原代码的核心问题:
- 文本分割逻辑有误,按空格分割后循环处理,会忽略中间位置输入的
@,仅判断最后一段内容; - 正则用
^限制行首,只能匹配以@开头的整段内容,无法匹配文本中间的@。
修正后的代码(基础版)
void onChanged(String value) { // 匹配文本末尾以@开头,后跟字母、数字、下划线的内容(排除空格结尾的情况) final mentionMatch = RegExp(r'@[a-zA-Z0-9_]*$').firstMatch(value); if (mentionMatch != null) { // 提取@后的内容用于搜索用户 final matchedText = mentionMatch.group(0)!.substring(1); state = state.copyWith(matched: matchedText); toggleMentionWidget(true); print('MENTION => $matchedText'); } else { // 无有效@输入状态,隐藏组件 toggleMentionWidget(false); state = state.copyWith(matched: ''); } }
代码说明
- 正则
r'@[a-zA-Z0-9_]*$'专门匹配文本末尾的@开头内容,不管@在文本的哪个位置,只要用户输入@后还没输入空格,就会触发匹配; - 匹配成功时显示组件并更新搜索关键词,匹配失败则隐藏组件。
进阶版(输入@ 后才隐藏组件)
如果需要严格实现「输入空格后隐藏」的逻辑(即用户输入@ 后才关闭组件),可以调整正则并增加判断:
void onChanged(String value) { final mentionMatch = RegExp(r'@[a-zA-Z0-9_]*\s?$').firstMatch(value); if (mentionMatch != null) { final matchedContent = mentionMatch.group(0)!; // 若匹配内容以空格结尾,说明用户结束提及输入 if (matchedContent.endsWith(' ')) { toggleMentionWidget(false); state = state.copyWith(matched: ''); } else { final matchedText = matchedContent.substring(1); state = state.copyWith(matched: matchedText); toggleMentionWidget(true); print('MENTION => $matchedText'); } } else { toggleMentionWidget(false); state = state.copyWith(matched: ''); } }
这个版本可以完美满足需求:
- 文本任意位置输入
@都会触发显示提及组件; - 输入空格后(
@后跟空格),自动隐藏组件。
内容的提问来源于stack exchange,提问作者MElochukwu
相关产品推荐
相关产品推荐

