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
相关产品推荐
相关产品推荐

