如何结合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
相关产品推荐
相关产品推荐

