You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Flutter的ChatBox组件中结合Socket.IO实现消息即时展示

问题解决:Flutter Socket.IO聊天消息即时展示与渲染错误修复

问题背景

使用Flutter结合Socket.IO开发聊天应用时,ChatBox组件存在两个核心问题:

  • 收到的新消息无法实时更新到UI
  • 消息渲染逻辑错误,展示内容不符合预期

修复方案

针对上述问题,对代码做以下关键修改:

1. 正确解析Socket消息数据

在chat_list_message事件回调中,恢复并完善消息解析逻辑,确保从Socket推送的消息列表中正确提取每条消息并添加到本地消息数组。

2. 触发UI即时更新

在收到新消息后调用setState,通知Flutter框架重新构建UI,实现消息的实时展示。

3. 修正消息渲染逻辑

调整ListView中的消息渲染代码,使用消息自身的username而非组件传入的widget.username,确保每条消息显示对应的发送者名称。

完整修正代码

class ChatBox extends StatefulWidget {
  const ChatBox({super.key, required this.liveID, required this.username});

  final int liveID;
  final String username;

  @override
  State<ChatBox> createState() => _ChatBoxState();
}

class _ChatBoxState extends State<ChatBox> {
  final TextEditingController _messageController = TextEditingController();
  bool _showEmojiPicker = false;
  late IO.Socket socket;
  int? currentUserID;
  String? currentUsername;
  List<ChatMessage> _messages = [];
  final StreamController<String> _streamController = StreamController<String>();
  Stream<String> get messagesStream => _streamController.stream;

  void _onEmojiSelected(Emoji emoji) {
    _messageController
      ..text += emoji.emoji
      ..selection = TextSelection.fromPosition(
        TextPosition(offset: _messageController.text.length),
      );
  }

  void _onBackspacePressed() {
    _messageController
      ..text = _messageController.text.characters.skipLast(1).toString()
      ..selection = TextSelection.fromPosition(
        TextPosition(offset: _messageController.text.length),
      );
  }

  @override
  void initState() {
    super.initState();
    _loadCurrentUserID();
    _initializeSocket();
  }

  @override
  void dispose() {
    _messageController.dispose();
    _streamController.close();
    socket.disconnect();
    super.dispose();
  }

  Future<void> _loadCurrentUserID() async {
    final SharedPreferences prefs = await SharedPreferences.getInstance();
    setState(() {
      currentUserID = prefs.getInt('loginUserId');
      currentUsername = prefs.getString('username');
    });
  }

  void _initializeSocket() {
    socket = IO.io('https://socket.example.com', <String, dynamic>{
      'transports': ['websocket'],
      'autoConnect': false,
    });

    socket.connect();

    socket.onConnect((_) {
      debugPrint('connected to websocket');
    });

    socket.onDisconnect((_) {
      debugPrint('disconnected from websocket');
    });

    socket.on('chat_list_message', (data) {
      debugPrint('Received message: $data');
      setState(() {
        if (data is Map && data['message'] is List && data['message'].isNotEmpty) {
          for (var messageObj in data['message']) {
            if (messageObj is Map && messageObj.containsKey('message')) {
              String messageContent = messageObj['message'];
              _messages.add(ChatMessage(
                userID: messageObj['userID'],
                liveID: messageObj['liveID'],
                message: messageContent,
                username: messageObj['username'],
              ));
              debugPrint('Received Message: $messageContent');
            } else {
              debugPrint('Unexpected message object: $messageObj');
            }
          }
        }
      });
    });

    socket.onError((error) {
      debugPrint('Socket error: $error');
    });
  }

  void _sendMessage() {
    String message = _messageController.text.trim();
    if (message.isNotEmpty) {
      final newMessage = {
        'userID': currentUserID,
        'liveID': widget.liveID,
        'message': message,
        'username': currentUsername ?? 'Unknown',
        'time': DateTime.now().toIso8601String(),
      };
      setState(() {
        _messages.add(ChatMessage.fromJson(newMessage));
      });
      socket.emit('add_chat_message', newMessage);
      _messageController.clear();
    }
  }

  @override
  Widget build(BuildContext context) {
    return BlocListener<ChatDataLiveBloc, ChatDataLiveState>(
      listener: (context, state) {
        if (state is ChatDataLiveSuccess) {
          setState(() {
            _messages = state.allChatDataLives ?? [];
          });
        } else if (state is ChatDataLiveFail) {
          ScaffoldMessenger.of(context).showSnackBar(
            SnackBar(content: Text(state.message)),
          );
        }
      },
      child: SizedBox(
        height: 150,
        child: Column(
          children: [
            Padding(
              padding: const EdgeInsets.all(8.0),
              child: Row(
                children: [
                  Expanded(
                    child: Divider(
                      color: Theme.of(context).primaryColor,
                      height: 1,
                      thickness: 1.5,
                    ),
                  ),
                  const SizedBox(width: 8.0),
                  Text(
                    'New Message',
                    overflow: TextOverflow.ellipsis,
                    style: Theme.of(context).textTheme.bodySmall,
                    maxLines: 1,
                  ),
                  const SizedBox(width: 8.0),
                  Expanded(
                    child: Divider(
                      color: Theme.of(context).primaryColor,
                      height: 1,
                      thickness: 1.5,
                    ),
                  ),
                ],
              ),
            ),
            Expanded(
              child: ListView.builder(
                itemCount: _messages.length,
                itemBuilder: (context, index) {
                  final message = _messages[index];
                  return Row(
                    children: [
                      const SizedBox(width: 20),
                      Text(
                        '${message.username} : ',
                        overflow: TextOverflow.ellipsis,
                        style: Theme.of(context).textTheme.bodyMedium,
                        maxLines: 1,
                      ),
                      const SizedBox(width: 10),
                      Text(
                        message.message ?? '',
                        overflow: TextOverflow.ellipsis,
                        style: Theme.of(context).textTheme.bodySmall,
                        maxLines: 1,
                      ),
                    ],
                  );
                },
              ),
            ),
            Container(
              margin: const EdgeInsets.only(left: 12.0, top: 12),
              child: Row(
                children: [
                  Expanded(
                    child: TextField(
                      controller: _messageController,
                      decoration: const InputDecoration(
                        hintText: 'Type your message...',
                        border: OutlineInputBorder(),
                      ),
                    ),
                  ),
                  const SizedBox(width: 8.0),
                  IconButton(
                    icon: const Icon(Icons.emoji_emotions),
                    onPressed: () {
                      setState(() {
                        _showEmojiPicker = !_showEmojiPicker;
                      });
                    },
                  ),
                  IconButton(
                    icon: const Icon(Icons.send),
                    onPressed: () {
                      _sendMessage();
                    },
                  ),
                ],
              ),
            ),
            Offstage(
              offstage: !_showEmojiPicker,
              child: SizedBox(
                height: 250,
                child: EmojiPicker(
                  onEmojiSelected: (category, emoji) {
                    _onEmojiSelected(emoji);
                  },
                  onBackspacePressed: _onBackspacePressed,
                  config: const Config(
                    height: 200,
                    checkPlatformCompatibility: true,
                    emojiViewConfig: EmojiViewConfig(
                      emojiSizeMax: 32,
                    ),
                    swapCategoryAndBottomBar: false,
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

内容的提问来源于stack exchange,提问作者cadeting

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 00:32:07