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

Flutter socket_io_client登录/登出后无法重新加入房间问题

Flutter Socket.IO 重登后无法加入房间的问题修复

核心问题分析

你的代码存在以下几个关键问题导致重登失败:

  1. 未等待Socket连接建立就发送事件:_initializeSocket里直接调用socket.emit,但此时Socket可能还处于连接握手阶段,事件会发送失败
  2. 旧Socket实例未彻底清理:登出时仅调用disconnect,但旧的Socket对象仍然存在,重登创建新实例时可能出现状态冲突
  3. Socket实例重置不彻底:重登时直接覆盖socket变量,没有确保旧实例完全销毁

修复后的完整代码

class AppBloc extends Bloc<AppEvent, AppState> with ChangeNotifier {
  AppBloc({required AuthService authenticationRepository, bool authenticated = false})
      : _authenticationRepository = authenticationRepository,
        super(
          authenticated
              ? AppState.authenticated(authenticationRepository.currentUser)
              : const AppState.unauthenticated(),
        ) {
    on<AppUserChanged>(_onUserChanged);
    on<AppLogoutRequested>(_onLogoutRequested);
    _userSubscription = _authenticationRepository.user.listen(
      (user) {
        add(AppUserChanged(user));
        notifyListeners();
      },
    );

    if (authenticated && _authenticationRepository.currentUser.isNotEmpty) {
      _initializeSocket(authenticationRepository.currentUser);
    }
  }

  void _initializeSocket(User currentUser) {
    // 初始化前先清理可能存在的旧socket
    _cleanupSocket();

    socket = IO.io('http://localhost:3000', <String, dynamic>{
      'transports': ['websocket'],
    });

    // 等待连接成功后再发送加入房间事件
    socket.onConnect((_) {
      socket.emit('joinUserRoom', currentUser.id);
      print('Socket连接成功,已加入用户房间:${currentUser.id}');
    });

    // 监听连接错误,便于调试
    socket.onConnectError((error) {
      print('Socket连接错误:$error');
    });
  }

  IO.Socket? socket; // 改为可空类型,便于置空清理

  final AuthService _authenticationRepository;
  late StreamSubscription<User> _userSubscription;

  void _onUserChanged(AppUserChanged event, Emitter<AppState> emit) {
    if (event.user.isNotEmpty) {
      _initializeSocket(event.user);
    } else {
      _cleanupSocket();
    }

    emit(
      event.user.isNotEmpty ? AppState.authenticated(event.user) : const AppState.unauthenticated(),
    );

    notifyListeners();
  }

  // 彻底清理socket:断开连接、销毁实例、置空变量
  void _cleanupSocket() {
    if (socket != null) {
      if (socket!.connected) {
        socket!.disconnect();
      }
      socket!.dispose();
      socket = null;
    }
  }

  void _onLogoutRequested(AppLogoutRequested event, Emitter<AppState> emit) {
    _cleanupSocket();
    _authenticationRepository.logOut();
    emit(const AppState.unauthenticated());
    notifyListeners();
  }

  @override
  Future<void> close() {
    _cleanupSocket();
    _userSubscription.cancel();
    return super.close();
  }
}

关键修改说明

  1. 等待连接成功再发送事件:通过socket.onConnect回调确保Socket连接完成后再发送joinUserRoom,避免事件丢失
  2. 彻底清理Socket实例:新增_cleanupSocket方法,负责断开连接、调用dispose销毁实例,并将socket变量置空,彻底清除旧实例的影响
  3. Socket变量改为可空类型:将late IO.Socket socket改为IO.Socket? socket,便于在清理时置空,避免未初始化的问题
  4. 添加错误监听:增加onConnectError回调,便于调试连接过程中出现的问题
  5. 初始化前先清理:在_initializeSocket开头先调用_cleanupSocket,确保创建新Socket前没有残留的旧实例

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 20:13:14