如何在Flutter TextField中检测@符号并实现仅输入@时触发弹窗的功能
嘿,我帮你搞定这个自定义的@提及功能!不用第三方包也能完美实现,核心就是监听输入内容,精准触发弹窗~
自定义TextField的@提及弹窗实现方案
核心思路
我们要手动监听TextField的输入变化,判断用户是否输入了@符号(避免连续输入@重复触发),然后控制弹窗的显示/隐藏,同时处理选择用户后的文本插入逻辑,完全贴合你的需求。
具体实现步骤(以Flutter为例)
1. 准备状态变量
先定义几个关键状态来控制整个流程:
TextEditingController:用来管理TextField的输入内容bool _showMentionPopup:标记弹窗是否显示int _mentionStartIndex:记录@符号的位置,方便后续插入用户昵称- 模拟用户列表:用来展示可选的提及用户
2. 监听输入,触发弹窗逻辑
给TextField的onChanged回调添加判断逻辑:只有当用户输入的最后一个字符是@,且前一个字符不是@时,才显示弹窗;其他情况(比如删除@、输入非@字符)则关闭弹窗。
3. 弹窗布局与交互
用Stack将TextField和弹窗组合,把弹窗定位在TextField上方;用户选择列表中的用户后,自动将@替换为@用户名,并调整光标位置到文本末尾,提升体验。
完整可运行代码示例
class MentionTextField extends StatefulWidget { const MentionTextField({super.key}); @override State<MentionTextField> createState() => _MentionTextFieldState(); } class _MentionTextFieldState extends State<MentionTextField> { final TextEditingController _textController = TextEditingController(); bool _showMentionPopup = false; int _mentionStartIndex = -1; // 模拟业务中的用户列表 final List<String> _userList = ["张三", "李四", "王五", "赵六"]; @override void dispose() { _textController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.all(16.0), child: Stack( children: [ TextField( controller: _textController, onChanged: (inputValue) { bool shouldShow = false; if (inputValue.isNotEmpty) { final lastChar = inputValue[inputValue.length - 1]; // 判断是否触发弹窗:最后一个字符是@,且不是连续输入的@ if (lastChar == '@' && (inputValue.length == 1 || inputValue[inputValue.length - 2] != '@')) { shouldShow = true; _mentionStartIndex = inputValue.length - 1; } } setState(() { _showMentionPopup = shouldShow; }); }, decoration: const InputDecoration( hintText: "输入@提及用户", border: OutlineInputBorder(), ), ), // 提及弹窗 if (_showMentionPopup) Positioned( top: -(_userList.length * 56), // 根据用户数量动态调整弹窗高度 left: 0, right: 0, child: Material( elevation: 4, borderRadius: BorderRadius.circular(8), child: Column( children: _userList.map((userName) { return ListTile( title: Text(userName), onTap: () { // 将@替换为@用户名,并调整光标位置 String newText = _textController.text.replaceRange( _mentionStartIndex, _mentionStartIndex + 1, "@$userName ", ); _textController.text = newText; _textController.selection = TextSelection.fromPosition( TextPosition(offset: newText.length), ); setState(() { _showMentionPopup = false; }); }, ); }).toList(), ), ), ), ], ), ); } }
可扩展优化点
- 支持@后输入字符过滤用户:比如用户输入@张,自动筛选出名字带张的用户
- 动态计算弹窗位置:通过
RenderBox获取TextField的实际位置,让弹窗更精准地对齐 - 处理光标在中间输入@的情况:如果用户在文本中间插入@,也能触发弹窗
内容的提问来源于stack exchange,提问作者Gbenga B Ayannuga
相关产品推荐
相关产品推荐

