0

I have a chat-app that uses a single TextField controlled by a TextEditingController to enter text messages. Pressing the associated IconButton sends the message if the message is not empty and then clears the TextEditingController. This all works perfectly. After sending the message, the text input field gets cleared.

BUT, here comes the bug, if I press the send button again, the message is sent once more. How come and how can I prevent this?

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,
          )
        ],
      ),
    );
  }
}

    
4

3 回答 3

0

我发现这里最简单的解决方案是更换

 onPressed: _enteredMessage.trim().isEmpty ? null : _sendMessage,

 onPressed: () {
            if (_controller.text.trim().isNotEmpty) _sendMessage();
          }
         
于 2021-03-11T01:19:38.327 回答
0

对 TextField使用 TextEditingControlleronChanged 事件可能会出现问题。此处深入讨论了该问题:TextEditingController vs OnChanged

就我而言,我最终决定使用 TextEditingController 的解决方案。这样,我们可以一起摆脱_enteredMessage变量和onChanged/setState()语句。

相反,我们需要向 TextEditingController 添加一个监听器并调用setState()我们的initState()方法。

最后,我们需要_controllerdispose()方法中配置 防止内存泄漏。

这是我的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,
      '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...'),
            ),
          ),
          IconButton(
            color: Theme.of(context).primaryColor,
            icon: Icon(
              Icons.send,
            ),
            onPressed: _controller.text.trim().isEmpty ? null : _sendMessage,
          ),
        ],
      ),
    );
  }
}

    
于 2021-03-26T05:28:59.257 回答
0

您正在清除按钮 callbkack 中的控制器 _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
...
于 2021-03-08T17:44:38.167 回答