Flutter - 输入 TextField 被 TextEditingController 清除,但输入的消息似乎是 'still alive'(不为空)

Flutter - Input TextField gets cleared by TextEditingController but the entered message appears to be 'still alive' (not empty)

我有一个聊天应用程序,它使用由 TextEditingController 控制的单个 TextField 来输入文本消息。 按关联的 IconButton 发送消息 如果消息不为空 然后清除 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 解决方案。这样,我们就可以完全摆脱 _enteredMessage 变量和 onChanged/setState() 语句。

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

最后,我们需要在dispose()方法中配置_controller,防止内存泄露

这是我的 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,
          ),
        ],
      ),
    );
  }
}