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

Flutter中如何让StompClient连接支持Future的async/await特性?

问题描述

我用StompClient连接游戏服务器的WebSocket端点,想让这个客户端支持async/await以便配合Flutter的FutureBuilder使用,于是做了如下封装:

class CombatClient {
  late StompClient _client;

  Future<bool> connect() async {
    _client = StompClient(
      config: StompConfig.SockJS(
        url: '${ServerGlobals.backendHost}/connect',
        onConnect: (frame) async {
          _client.subscribe(destination: "/gameserver/foo", callback: (data) => {print("foo: ${data.body}")});
        },
      ),
    );
    _client.activate();
    return await Future.doWhile(() => _client.connected);
  }
}

主逻辑里这么用:

final combatClient = CombatClient();
await combatClient.connect();

运行时抛出错误:

E/flutter (23147): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: type 'Null' is not a subtype of type 'FutureOr<bool>'
E/flutter (23147): #0      CombatClient.connect (package:app/combat/websocket/combat_client.dart:19:12)
E/flutter (23147): <asynchronous suspension>
E/flutter (23147): #1      _DebugCombatPageState.build.<anonymous closure> (package:app/debug/debug_combat_page.dart:61:27)
E/flutter (23147): <asynchronous suspension>
E/flutter (23147):

不管这个错误,我想知道让游戏服务器连接支持await的正确实现方式,总是在Future相关操作上踩坑,找不到合适的API用法。


正确实现方式

错误原因梳理

你当前代码的问题出在Future.doWhile的误用:doWhile要求回调返回FutureOr<bool>,且会循环执行回调直到返回false,但它并不适合用来等待一次性的连接状态变更。更关键的是,当_client.connected的状态出现非预期值(比如内部返回null)时,就会触发类型转换错误。

标准实现方案:用Completer封装回调式异步操作

处理回调转async/await的标准方式是使用Completer,它可以手动控制Future的完成时机,完美适配StompClient的回调式连接逻辑:

import 'dart:async';
import 'package:stomp_dart_client/stomp_dart_client.dart';

class CombatClient {
  late StompClient _client;
  Completer<bool>? _connectCompleter;

  Future<bool> connect() async {
    // 避免重复发起连接请求
    if (_connectCompleter?.isCompleted == false) {
      return _connectCompleter!.future;
    }

    _connectCompleter = Completer<bool>();

    _client = StompClient(
      config: StompConfig.SockJS(
        url: '${ServerGlobals.backendHost}/connect',
        onConnect: (frame) {
          // 连接成功后完成Completer,同时订阅目标主题
          _client.subscribe(
            destination: "/gameserver/foo",
            callback: (data) => print("foo: ${data.body}")
          );
          _connectCompleter?.complete(true);
        },
        onWebSocketError: (error) {
          // 连接失败时完成Completer,返回false或抛出异常
          _connectCompleter?.complete(false);
          // 若需要让调用者捕获异常,可替换为:
          // _connectCompleter?.completeError(error);
        },
        onDisconnect: (frame) {
          // 断开连接时重置Completer,允许后续重新连接
          _connectCompleter = null;
        },
      ),
    );

    _client.activate();

    // 返回Future,等待连接结果
    return _connectCompleter!.future;
  }

  // 可选:添加断开连接方法
  void disconnect() {
    _client.deactivate();
    _connectCompleter?.complete(false);
    _connectCompleter = null;
  }
}

调用方式

和之前的用法一致,但现在能正确await连接完成,还能处理异常情况:

final combatClient = CombatClient();
try {
  bool isConnected = await combatClient.connect();
  if (isConnected) {
    print("游戏服务器连接成功");
    // 连接成功后的业务逻辑
  } else {
    print("游戏服务器连接失败");
  }
} catch (e) {
  // 捕获连接异常(如果在Completer中使用了completeError)
  print("连接出错:$e");
}

适配FutureBuilder的示例

如果要配合FutureBuilder使用,可以在页面状态中维护连接Future:

class _DebugCombatPageState extends State<DebugCombatPage> {
  late Future<bool> _connectFuture;
  final CombatClient _combatClient = CombatClient();

  @override
  void initState() {
    super.initState();
    _connectFuture = _combatClient.connect();
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<bool>(
      future: _connectFuture,
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.waiting) {
          return const CircularProgressIndicator();
        }
        if (snapshot.hasError) {
          return Text("连接失败:${snapshot.error}");
        }
        if (snapshot.data == true) {
          return const Text("连接成功");
        } else {
          return const Text("连接失败");
        }
      },
    );
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 22:00:57