问题描述
我有一个聊天应用程序,它使用由 TextEditingController 控制的单个 TextField 来输入文本消息。 按关联的图标按钮发送消息如果消息不为空,然后清除 TextEditingController。这一切都完美无缺。发送消息后,文本输入字段被清除。
但是,错误来了,如果我再次按下发送按钮,消息会再次发送。这是怎么回事,我该如何预防?
class NewMessage extends StatefulWidget {
@override
_NewMessageState createState() => _NewMessageState();
}
class _NewMessageState extends State<NewMessage> {
final _controller = TextEditingController();
var _enteredMessage = '';
void _sendMessage() async {
FocusScope.of(context).unfocus();
final user = FirebaseAuth.instance.currentUser;
final userData = await FirebaseFirestore.instance
.collection('users')
.doc(user.uid)
.get();
FirebaseFirestore.instance.collection('chat').add({
'text': _enteredMessage,'createdAt': Timestamp.Now(),'userId': user.uid,'username': userData.data()['username'],'userImage': userData.data()['image_url']
});
_controller.clear();
}
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.only(top: 8),padding: EdgeInsets.all(8),child: Row(
children: <Widget>[
Expanded(
child: TextField(
controller: _controller,textCapitalization: TextCapitalization.sentences,autocorrect: true,enableSuggestions: true,decoration: Inputdecoration(labelText: 'Send a message...'),onChanged: (value) {
setState(() {
_enteredMessage = value;
});
},),IconButton(
color: Theme.of(context).primaryColor,icon: Icon(
Icons.send,onpressed: _enteredMessage.trim().isEmpty ? null : _sendMessage,)
],);
}
}
解决方法
您正在清除按钮回调中的控制器
_controller.clear()
,但您真正发送到 Firebase 的不是 _controller
文本而是变量
_enteredMessage
没有被清除。
如果你只是发送控制器文本而不是 _enteredMessage
问题应该可以解决:
FirebaseFirestore.instance.collection('chat').add({
'text': _controller.text,'createdAt': Timestamp.now(),'userId': user.uid,'username': userData.data()['username'],'userImage': userData.data()['image_url']
});
同时始终在 Stateful Widget onDispose 方法中处理您的控制器以避免内存泄漏。
编辑: 调用按钮回调的条件也应更改为:
...
onPressed: _controller.text.isEmpty ? null : _sendMessage
...
,
我发现这里最简单的解决方案是更换
onPressed: _enteredMessage.trim().isEmpty ? null : _sendMessage,
与
onPressed: () {
if (_controller.text.trim().isNotEmpty) _sendMessage();
}
,
将 TextEditingController AND 用于 TextField 的 onChanged 事件可能会出现问题。该问题在此处进行了深入讨论:TextEditingController vs OnChanged
就我而言,我最终决定采用 TextEditingController 仅解决方案。这样,我们就可以去掉 _enteredMessage
变量和 onChanged/setState() 语句。
相反,我们需要向 TextEditingController 添加一个侦听器,并在 setState()
方法中调用 initState()
。
最后,我们需要处理 _controller
方法中的 dispose()
以防止内存泄漏。
这是我的TextEditingController 唯一解决方案的代码:
class NewMessage extends StatefulWidget {
@override
_NewMessageState createState() => _NewMessageState();
}
class _NewMessageState extends State<NewMessage> {
var _controller = TextEditingController();
@override
void initState() {
_controller = TextEditingController();
_controller.addListener(() {
setState(() {});
});
super.initState();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
void _sendMessage() async {
FocusScope.of(context).unfocus();
final user = FirebaseAuth.instance.currentUser;
final userData = await FirebaseFirestore.instance
.collection('users')
.doc(user.uid)
.get();
FirebaseFirestore.instance.collection('chat').add({
'text': _controller.text,'userImage': userData.data()['image_url']
});
_controller.clear();
}
@override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.only(top: 8),padding: EdgeInsets.all(8),child: Row(
children: <Widget>[
Expanded(
child: TextField(
controller: _controller,textCapitalization: TextCapitalization.sentences,autocorrect: true,enableSuggestions: true,decoration: InputDecoration(labelText: 'Send a message...'),),IconButton(
color: Theme.of(context).primaryColor,icon: Icon(
Icons.send,onPressed: _controller.text.trim().isEmpty ? null : _sendMessage,],);
}
}