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

Flutter客户端如何等待Socket.IO连接成功后获取socket.id

Flutter Socket.IO 登出后重连无法可靠获取socket.id 解决方案

问题背景

  • Flutter客户端集成Socket.IO,后端为基础Node.js服务
  • 初始在main.dart中调用socket.connect()可正常建立连接,获取socket.id完成模拟登录
  • 调用signout()时执行socket.disconnect()断开连接流程正常
  • 后续调用signin()重连时,原硬编码延迟的方案在网络波动时容易失效,还会造成不必要的等待,没有可靠机制等待连接建立完成、获取socket.id赋值给登录用户

核心实现思路

利用Dart的Completer手动管理连接完成的异步状态,在Socket的connect/reconnect事件触发时完成completer返回socket.id,连接报错时抛出异常,signin方法中直接await这个Future即可100%等待连接就绪,不需要写死等待时长。

完整实现代码

1. 修改SocketRepository

import 'dart:async';
import 'package:socket_io_client/socket_io_client.dart' as io;

class SocketRepository {
  late io.Socket socket;
  Auth auth = Auth.instance;

  // 用于管理连接完成状态的Completer
  Completer<String?> _socketIdCompleter = Completer();
  // 对外暴露等待连接完成、获取socketId的Future
  Future<String?> get socketId => _socketIdCompleter.future;

  static final SocketRepository _socketRepo = SocketRepository._internal();

  factory SocketRepository() {
    return _socketRepo;
  }

  SocketRepository._internal() {
    socket = io('http://127.0.0.1:3000', <String, dynamic>{
      'transports': ['websocket'],
      'autoConnect': false,
    });

    // 连接成功回调
    socket.onConnect((_) {
      print('socket connected: ${socket.id}');
      auth.socketId = socket.id;
      // 连接成功时完成completer,返回socketId
      if (!_socketIdCompleter.isCompleted) {
        _socketIdCompleter.complete(socket.id);
      }
    });

    // 重连成功回调
    socket.on('reconnect', (_) {
      auth.socketId = socket.id;
      if (!_socketIdCompleter.isCompleted) {
        _socketIdCompleter.complete(socket.id);
      }
    });

    // 连接错误回调
    socket.on("error", (data) {
      if (!_socketIdCompleter.isCompleted) {
        _socketIdCompleter.completeError(data);
      }
    });

    // 断开连接回调
    socket.onDisconnect((_) {
      // 可在这里做连接断开后的清理逻辑
    });
  }

  void connect() {
    // 每次发起新连接前重置completer
    if (_socketIdCompleter.isCompleted) {
      _socketIdCompleter = Completer();
    }
    socket.connect();
  }

  void disconnect() {
    socket.disconnect();
    // 断开时如果还在等待连接结果,结束等待返回null
    if (!_socketIdCompleter.isCompleted) {
      _socketIdCompleter.complete(null);
    }
  }
}

2. 修改登录登出逻辑

class UserRepository {
  Auth auth = Auth.instance;
  final socketRepo = SocketRepository();

  Future<User> signIn() async {
    try {
      await Future.delayed(const Duration(seconds: 1));
      // 先判断当前是否已连接
      if (socketRepo.socket.id == null) {
        print('start reconnect socket');
        socketRepo.connect();
        // 等待连接完成,拿到socketId,不需要硬编码延迟
        final socketId = await socketRepo.socketId;
        auth.socketId = socketId;
      }
      final loginUser = User(
        id: 1, 
        socketId: socketRepo.socket.id, 
        userName: 'Logged in User'
      );
      auth.currentUser = loginUser;
      print('login success, user info: $loginUser');
      return auth.currentUser;
    } catch (e) {
      print('login error: $e');
      rethrow;
    }
  }

  Future<User> signOut() async {
    try {
      await Future.delayed(const Duration(seconds: 1));
      auth.currentUser = User.empty;
      socketRepo.disconnect();
      print('sign out success');
      return auth.currentUser;
    } catch (e) {
      print('sign out error: $e');
      rethrow;
    }
  }
}

方案优势

  • 不需要硬编码等待时长,连接成功后立即返回,弱网环境下也能稳定拿到socket.id
  • 连接出错时会直接抛出异常,方便上层做错误提示、重试逻辑
  • 单例SocketRepository全局管理连接状态,补全了completer状态判断,不会出现多次触发回调、内存泄漏问题
  • 自动适配首次连接、登出后重连、异常断连重连等多种场景

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 18:45:44