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

如何结合Freezed在Flutter Bloc中实现socket_io_client集成?

在Freezed+Flutter Bloc项目中集成socket_io_client的完整实现步骤

1. 安装依赖

在pubspec.yaml中添加所需依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_bloc: ^8.1.3
  socket_io_client: ^2.0.3+1
  freezed_annotation: ^2.4.1

dev_dependencies:
  flutter_test:
    sdk: flutter
  build_runner: ^2.4.6
  freezed: ^2.4.5

执行以下命令完成依赖安装:

flutter pub get

2. 封装Socket服务类

创建单例模式的SocketService,统一管理Socket连接、事件监听与消息发送逻辑:

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

class SocketService {
  static final SocketService _instance = SocketService._internal();
  factory SocketService() => _instance;
  SocketService._internal();

  late io.Socket _socket;
  bool get isConnected => _socket.connected;

  // 初始化并建立Socket连接
  void connect(String url) {
    _socket = io.io(url, <String, dynamic>{
      'transports': ['websocket'],
      'autoConnect': false,
    });
    _socket.connect();

    _socket.onConnect((_) => print('Socket连接成功'));
    _socket.onDisconnect((_) => print('Socket连接断开'));
    _socket.onError((error) => print('Socket错误: $error'));
  }

  // 断开Socket连接
  void disconnect() {
    if (isConnected) _socket.disconnect();
  }

  // 监听指定事件的消息
  void onMessage(String event, Function(dynamic data) callback) {
    _socket.on(event, callback);
  }

  // 发送消息到服务器
  void sendMessage(String event, dynamic data) {
    if (isConnected) _socket.emit(event, data);
  }
}

3. 用Freezed定义Bloc事件与状态

创建socket_bloc.dart文件,通过Freezed生成不可变的事件和状态:

import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

part 'socket_bloc.freezed.dart';

// Socket事件定义
@freezed
class SocketEvent with _$SocketEvent {
  const factory SocketEvent.connect(String url) = SocketConnect;
  const factory SocketEvent.disconnect() = SocketDisconnect;
  const factory SocketEvent.sendMessage(String event, dynamic data) = SocketSendMessage;
  const factory SocketEvent.receivedMessage(String event, dynamic data) = SocketReceivedMessage;
  const factory SocketEvent.error(String message) = SocketErrorEvent;
}

// Socket状态定义
@freezed
class SocketState with _$SocketState {
  const factory SocketState.initial() = SocketInitial;
  const factory SocketState.connected() = SocketConnected;
  const factory SocketState.disconnected() = SocketDisconnected;
  const factory SocketState.messageReceived(String event, dynamic data) = SocketMessageReceived;
  const factory SocketState.error(String message) = SocketError;
}

执行命令生成Freezed代码:

flutter pub run build_runner build

4. 实现Socket Bloc核心逻辑

在socket_bloc.dart中完成Bloc类的编写,处理事件并与SocketService交互:

class SocketBloc extends Bloc<SocketEvent, SocketState> {
  final SocketService socketService;

  SocketBloc({required this.socketService}) : super(const SocketState.initial()) {
    // 监听服务器推送的消息,转化为Bloc事件
    socketService.onMessage('new_message', (data) {
      add(SocketEvent.receivedMessage('new_message', data));
    });

    // 处理连接事件
    on<SocketConnect>((event, emit) {
      try {
        socketService.connect(event.url);
        emit(const SocketState.connected());
        // 监听连接断开,自动触发断开事件
        socketService._socket.onDisconnect((_) => add(const SocketEvent.disconnect()));
      } catch (e) {
        add(SocketEvent.error(e.toString()));
      }
    });

    // 处理断开连接事件
    on<SocketDisconnect>((event, emit) {
      socketService.disconnect();
      emit(const SocketState.disconnected());
    });

    // 处理发送消息事件
    on<SocketSendMessage>((event, emit) {
      socketService.isConnected
          ? socketService.sendMessage(event.event, event.data)
          : add(const SocketEvent.error('未连接到Socket服务器'));
    });

    // 处理收到消息事件
    on<SocketReceivedMessage>((event, emit) {
      emit(SocketState.messageReceived(event.event, event.data));
    });

    // 处理错误事件
    on<SocketErrorEvent>((event, emit) {
      emit(SocketState.error(event.message));
    });
  }

  @override
  Future<void> close() {
    socketService.disconnect();
    return super.close();
  }
}

5. UI层集成与使用

在页面中通过BlocProvider提供Bloc实例,并用BlocBuilder监听状态变化:

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

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

  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (context) => SocketBloc(socketService: SocketService()),
      child: Scaffold(
        appBar: AppBar(title: const Text('Socket Demo')),
        body: BlocBuilder<SocketBloc, SocketState>(
          builder: (context, state) {
            return Column(
              children: [
                // 连接状态展示
                state.when(
                  initial: () => const Text('未连接'),
                  connected: () => const Text('已连接', style: TextStyle(color: Colors.green)),
                  disconnected: () => const Text('已断开', style: TextStyle(color: Colors.red)),
                  messageReceived: (event, data) => Text('收到消息: $data'),
                  error: (message) => Text('错误: $message', style: TextStyle(color: Colors.red)),
                ),
                // 连接/断开按钮组
                Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    ElevatedButton(
                      onPressed: () => context.read<SocketBloc>().add(const SocketEvent.connect('http://your-socket-server-url')),
                      child: const Text('连接'),
                    ),
                    const SizedBox(width: 16),
                    ElevatedButton(
                      onPressed: () => context.read<SocketBloc>().add(const SocketEvent.disconnect()),
                      child: const Text('断开'),
                    ),
                  ],
                ),
                // 消息输入与发送区域
                Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Row(
                    children: [
                      Expanded(
                        child: TextField(
                          decoration: const InputDecoration(hintText: '输入消息'),
                          onSubmitted: (value) => context.read<SocketBloc>().add(SocketEvent.sendMessage('send_message', value)),
                        ),
                      ),
                      IconButton(
                        onPressed: () {
                          final controller = TextEditingController();
                          context.read<SocketBloc>().add(SocketEvent.sendMessage('send_message', controller.text));
                          controller.clear();
                        },
                        icon: const Icon(Icons.send),
                      ),
                    ],
                  ),
                ),
              ],
            );
          },
        ),
      ),
    );
  }
}

关键注意事项

  • Socket配置:根据服务器实际配置调整transports参数,推荐使用['websocket']适配多平台。
  • 生命周期管理:在Bloc的close方法中断开Socket连接,避免内存泄漏。
  • 错误处理:捕获Socket连接、消息发送过程中的异常,转化为Bloc错误状态并在UI层展示。
  • 代码生成:每次修改Freezed事件或状态后,必须重新运行build_runner生成代码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 12:21:48