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

如何在Flutter多页面复用单个WebSocket连接的数据?

解决方案:单例模式封装全局WebSocket服务

你可以通过单例模式封装WebSocket服务实现全局唯一连接,同时提供两种方式访问实时数据:供UI更新的Stream,以及供build外直接调用的最新数据快照,完全替代传参方案,代码可维护性和扩展性更强。

核心思路

单例模式确保整个App只有一个WebSocket连接实例,所有页面共享同一连接和数据。同时在单例中封装连接管理、消息监听、状态维护逻辑,避免重复代码。

具体实现步骤

1. 封装WebSocket单例服务

创建一个全局单例类,处理连接、消息接收、状态管理:

import 'package:web_socket_channel/io.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
import 'dart:async';
import 'dart:developer' as dev;

class WebSocketService {
  // 单例实例(全局唯一)
  static final WebSocketService _instance = WebSocketService._internal();
  factory WebSocketService() => _instance;
  WebSocketService._internal();

  late WebSocketChannel _channel;
  // 保存最新消息快照,供build外直接访问
  String? latestMessage;
  // 对外暴露的消息流,供StreamBuilder实时更新UI
  late Stream<String> messageStream;
  // 连接状态标记
  bool isConnected = false;

  // 初始化WebSocket连接
  void connect() {
    if (isConnected) return; // 避免重复连接
    WebSocket.connect("wss://MY_DOMAIN_URL", headers: {
      'Connection': 'Upgrade',
      'Upgrade': 'WebSocket',
    }).then((ws) {
      dev.log('SOCKET CONNECTED');
      _channel = IOWebSocketChannel(ws);
      isConnected = true;
      
      // 绑定消息流,同时更新最新消息快照
      messageStream = _channel.stream.map((message) {
        latestMessage = message.toString();
        dev.log('Received message: $latestMessage');
        return latestMessage!;
      });

      // 监听连接关闭事件
      ws.done.then((_) {
        dev.log('SOCKET DISCONNECTED');
        isConnected = false;
        // 可选:添加自动重连逻辑
        // Future.delayed(const Duration(seconds: 3), () => connect());
      });
    }).catchError((error) {
      dev.log('Connection error: $error');
      isConnected = false;
    });
  }

  // 发送消息(如果业务需要)
  void sendMessage(String message) {
    if (isConnected) {
      _channel.sink.add(message);
    } else {
      dev.log('Cannot send message: not connected');
    }
  }

  // 主动关闭连接
  void disconnect() {
    if (isConnected) {
      _channel.sink.close();
      isConnected = false;
    }
  }
}

2. 全局初始化连接

在App启动时初始化连接,确保所有页面加载前已建立连接:

void main() {
  // 初始化WebSocket连接
  WebSocketService().connect();
  runApp(const MyApp());
}

3. 页面中使用数据

方式一:用StreamBuilder实时更新UI

适合需要根据消息自动刷新UI的场景:

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: StreamBuilder<String>(
        stream: WebSocketService().messageStream,
        initialData: WebSocketService().latestMessage, // 初始化显示最新快照
        builder: (context, snapshot) {
          if (snapshot.hasData) {
            return Center(child: Text('最新消息:${snapshot.data}'));
          } else if (snapshot.hasError) {
            return Center(child: Text('连接错误:${snapshot.error}'));
          }
          return const Center(child: CircularProgressIndicator());
        },
      ),
    );
  }
}

方式二:在build外直接访问最新数据

适合业务逻辑处理(比如按钮点击、后台任务):

class SettingsPage extends StatefulWidget {
  const SettingsPage({super.key});

  @override
  State<SettingsPage> createState() => _SettingsPageState();
}

class _SettingsPageState extends State<SettingsPage> {
  late WebSocketService _wsService;

  @override
  void initState() {
    super.initState();
    _wsService = WebSocketService();
    // 可选:监听消息变化,处理业务逻辑
    _wsService.messageStream.listen((message) {
      dev.log('设置页收到消息:$message');
      // 这里可以执行非UI的业务逻辑,比如存储本地、调用API等
    });
  }

  void handleCheckLatestMessage() {
    // 直接获取最新消息快照
    String? latestMsg = _wsService.latestMessage;
    if (latestMsg != null) {
      dev.log('当前最新消息:$latestMsg');
      // 执行业务操作
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ElevatedButton(
          onPressed: handleCheckLatestMessage,
          child: const Text('获取最新消息'),
        ),
      ),
    );
  }
}

方案优势对比

  • 比起全局变量:单例封装了连接逻辑、状态管理,避免全局变量的混乱,便于统一处理重连、错误等事件
  • 比起Navigator传参:无需在页面间层层传递数据,所有页面直接调用单例即可,扩展性更强
  • 同时支持UI实时更新和build外业务逻辑访问,覆盖所有使用场景

注意事项

  • 可以添加自动重连逻辑(代码中已注释),提升稳定性
  • 用户退出登录等场景下,记得主动调用disconnect()关闭连接并清空latestMessage
  • 如果项目已使用Provider/Riverpod等状态管理库,也可以将单例集成到状态管理中,进一步优化状态共享

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 00:21:26