Flutter实时通信插件socket_io_client_flutter的使用

发布于 1周前 作者 yibo5220 来自 Flutter

Flutter实时通信插件socket_io_client_flutter的使用

现在,socket_io_client_flutter 可以作为 Flutter 包从 pub.dev 获取。

版本信息

2.x.x 版本 支持完全的空安全(sound null safety)。可以在 pubspec.yaml 文件中这样添加:

dependencies:
  socket_io_client_flutter: ^2.0.0

Dart 3 需要完全的空安全,并且不支持非空安全或不完全的空安全。使用 Dart 3 的 Flutter 版本只能与 socket_io_client_flutter 2.x.x 版本一起工作。使用 Dart 2.12 及以上版本的 Flutter 版本也可以与 socket_io_client_flutter 2.x.x 版本一起工作。

1.x.x 版本 不支持空安全,兼容非空安全和不完全的空安全。可以在 pubspec.yaml 文件中这样添加:

dependencies:
  socket_io_client_flutter: ^1.0.2

Dart 1 和 Dart 2 需要非空安全。Dart 2.12 及以上版本支持非空安全以及完全的和不完全的空安全。一些早期使用 Dart 2.12 及以上版本的 Flutter 版本可以自动运行 socket_io_client_flutter 1.x.x 版本,但更新的 Flutter 版本需要使用 --no-sound-null-safety 参数来运行 socket_io_client_flutter 1.x.x 版本。

原始发布说明:将 socket.io-client-dart 移植到 Flutter 而不包含空安全,兼容 Flutter 1.22.6(无空安全)和 Flutter 3.0.2(支持混合空安全)。为 xibbit 添加了无害的长轮询扩展。

在 1.x.x 和 2.x.x 版本中,导入代码如下:

import 'package:socket_io_client_flutter/socket_io_client_flutter.dart';

socket.io-client-dart

这是一个将优秀的 JavaScript Node.js 库 - Socket.io-client v2.0.1~v3.0.3 - 移植到 Dart 的库。

版本信息
socket.io-client-dart Socket.io Server
v0.9.* ~ v1.* v2.*
v2.* v3.* & v4.*

使用

Dart Server
import 'package:socket_io/socket_io.dart';

void main() {
  // Dart server
  var io = Server();
  var nsp = io.of('/some');
  nsp.on('connection', (client) {
    print('connection /some');
    client.on('msg', (data) {
      print('data from /some => $data');
      client.emit('fromServer', "ok 2");
    });
  });
  io.on('connection', (client) {
    print('connection default namespace');
    client.on('msg', (data) {
      print('data from default => $data');
      client.emit('fromServer', "ok");
    });
  });
  io.listen(3000);
}
Dart Client
import 'package:socket_io_client/socket_io_client.dart' as IO;

void main() {
  // Dart client
  IO.Socket socket = IO.io('http://localhost:3000');
  socket.onConnect((_) {
    print('connect');
    socket.emit('msg', 'test');
  });
  socket.on('event', (data) => print(data));
  socket.onDisconnect((_) => print('disconnect'));
  socket.on('fromServer', (_) => print(_));
}
手动连接

要手动连接套接字,设置选项 autoConnect: false 并调用 .connect() 方法。

例如:

IO.Socket socket = IO.io('http://localhost:3000', 
    OptionBuilder()
      .setTransports(['websocket']) // for Flutter or Dart VM
      .disableAutoConnect()  // disable auto-connection
      .setExtraHeaders({'foo': 'bar'}) // optional
      .build()
  );
socket.connect();

注意:如果 autoConnect: true (默认情况下启用),则不应调用 .connect(),否则会导致所有事件处理器被注册并触发两次。参见 Issue #33

更新额外头
IO.Socket socket = ... // 创建套接字。
socket.io.options['extraHeaders'] = {'foo': 'bar'}; // 更新额外头。
socket.io.disconnect().connect(); // 手动重新连接套接字。
带确认的发送
IO.Socket socket = ... // 创建套接字。
socket.onConnect((_) {
    print('connect');
    socket.emitWithAck('msg', 'init', ack: (data) {
        print('ack $data') ;
        if (data != null) {
          print('from server $data');
        } else {
          print("Null") ;
        }
    });
});
套接字连接事件

这些事件可以监听。

const List EVENTS = [
  'connect',
  'connect_error',
  'connect_timeout',
  'connecting',
  'disconnect',
  'error',
  'reconnect',
  'reconnect_attempt',
  'reconnect_failed',
  'reconnect_error',
  'reconnecting',
  'ping',
  'pong'
];

// 替换 'onConnect' 为上述任意一个事件。
socket.onConnect((_) {
    print('connect');
});
确认套接字服务器已收到事件
socket.on('eventName', (data) {
    final dataList = data as List;
    final ack = dataList.last as Function;
    ack(null);
});

在 Flutter 中使用

在 Flutter 环境中(不包括 Flutter Web 环境),它只支持 dart:io WebSocket,而不是 dart:html WebSocket 或 Ajax(XHR),因此在这种情况下,创建套接字实例时必须添加 setTransports(['websocket'])

例如:

IO.Socket socket = IO.io('http://localhost:3000',
  OptionBuilder()
      .setTransports(['websocket']) // for Flutter or Dart VM
      .setExtraHeaders({'foo': 'bar'}) // optional
      .build());

在 Flutter 中使用 Stream 和 StreamBuilder

import 'dart:async';

// STEP1: 设置 Stream
class StreamSocket {
  final _socketResponse = StreamController<String>();

  void Function(String) get addResponse => _socketResponse.sink.add;

  Stream<String> get getResponse => _socketResponse.stream;

  void dispose() {
    _socketResponse.close();
  }
}

StreamSocket streamSocket = StreamSocket();

// STEP2: 在 main.dart 文件的 main 函数中添加此函数并将传入的数据添加到流中
void connectAndListen() {
  IO.Socket socket = IO.io('http://localhost:3000',
      OptionBuilder()
       .setTransports(['websocket']).build());

    socket.onConnect((_) {
     print('connect');
     socket.emit('msg', 'test');
    });

    // 当从服务器接收到事件时,数据被添加到流中
    socket.on('event', (data) => streamSocket.addResponse);
    socket.onDisconnect((_) => print('disconnect'));
}

// STEP3: 使用 StreamBuilder 构建小部件

class BuildWithSocketStream extends StatelessWidget {
  const BuildWithSocketStream({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      child: StreamBuilder(
        stream: streamSocket.getResponse,
        builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
          return Container(
            child: snapshot.hasData ? Text(snapshot.data) : Text('No data'),
          );
        },
      ),
    );
  }
}

故障排除

无法连接 “https” 服务器或自签名证书服务器

有关详细信息,请参阅 此问题

解决方法是使用由 @lehno 提供的以下代码:

class MyHttpOverrides extends HttpOverrides {
  @override
  HttpClient createHttpClient(SecurityContext context) {
    return super.createHttpClient(context)
      ..badCertificateCallback =
          (X509Certificate cert, String host, int port) => true;
  }
}

void main() {
  HttpOverrides.global = MyHttpOverrides();
  runApp(MyApp());
}
iOS 关闭套接字时的内存泄漏问题

有关详细信息,请参阅 此问题

请使用 socket.dispose() 代替 socket.close()socket.disconnect() 来解决 iOS 上的内存泄漏问题。

macOS 上出现 Connect_error 错误

有关详细信息,请参阅 此问题

通过将以下键添加到 *.entitlements 文件中来解决问题:

<key>com.apple.security.network.client</key>
<true/>

更多关于Flutter实时通信插件socket_io_client_flutter的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter实时通信插件socket_io_client_flutter的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个使用 socket_io_client_flutter 插件在 Flutter 中实现实时通信的示例代码。这个示例展示了如何连接到 Socket.IO 服务器、发送和接收消息。

首先,确保你已经在 pubspec.yaml 文件中添加了 socket_io_client_flutter 依赖:

dependencies:
  flutter:
    sdk: flutter
  socket_io_client_flutter: ^2.0.0-beta.4-nullsafety.0  # 请根据需要检查最新版本

然后运行 flutter pub get 以获取依赖。

接下来,在你的 Flutter 项目中创建一个简单的实时通信示例。

main.dart

import 'package:flutter/material.dart';
import 'package:socket_io_client_flutter/socket_io_client.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Socket.IO Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: SocketIODemo(),
    );
  }
}

class SocketIODemo extends StatefulWidget {
  @override
  _SocketIODemoState createState() => _SocketIODemoState();
}

class _SocketIODemoState extends State<SocketIODemo> {
  late SocketIO socket;
  final TextEditingController _messageController = TextEditingController();
  final List<String> _messages = [];

  @override
  void initState() {
    super.initState();
    initSocket();
  }

  void initSocket() {
    socket = io('http://your-socket-io-server-url:port', <String, dynamic>{
      'transports': ['websocket'],
    });

    socket.onConnect((_) {
      print('Connected to server');
      _messages.add('Connected to server');
      setState(() {});
    });

    socket.onDisconnect((_) {
      print('Disconnected from server');
      _messages.add('Disconnected from server');
      setState(() {});
    });

    socket.onMessage((data) {
      print('Message from server: $data');
      _messages.add('Server: $data');
      setState(() {});
    });

    socket.onError((error) {
      print('Error: $error');
      _messages.add('Error: $error');
      setState(() {});
    });
  }

  void _sendMessage() {
    if (_messageController.text.isNotEmpty) {
      socket.emit('message', _messageController.text);
      _messages.add('You: ${_messageController.text}');
      _messageController.clear();
      setState(() {});
    }
  }

  @override
  void dispose() {
    socket.disconnect();
    _messageController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Socket.IO Demo'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Expanded(
              child: ListView.builder(
                itemCount: _messages.length,
                itemBuilder: (_, index) {
                  return Text(_messages[index], style: TextStyle(fontSize: 18));
                },
              ),
            ),
            TextField(
              controller: _messageController,
              decoration: InputDecoration(
                border: OutlineInputBorder(),
                labelText: 'Enter message',
              ),
              onSubmitted: _sendMessage,
            ),
            ElevatedButton(
              onPressed: _sendMessage,
              child: Text('Send'),
            ),
          ],
        ),
      ),
    );
  }
}

说明

  1. 连接到 Socket.IO 服务器

    • 使用 io('http://your-socket-io-server-url:port', options) 方法连接到 Socket.IO 服务器。
    • options 参数允许你配置传输方式等。在这个示例中,我们只指定了 transports: ['websocket']
  2. 事件监听

    • socket.onConnect:当连接到服务器时触发。
    • socket.onDisconnect:当从服务器断开连接时触发。
    • socket.onMessage:当从服务器接收到消息时触发。
    • socket.onError:当发生错误时触发。
  3. 发送消息

    • 使用 socket.emit('event', data) 方法发送消息。在这个示例中,我们发送一个名为 message 的事件,数据为输入框中的内容。
  4. UI

    • 使用 ListView.builder 显示消息历史。
    • 使用 TextFieldElevatedButton 发送消息。

请确保将 'http://your-socket-io-server-url:port' 替换为你的 Socket.IO 服务器的实际 URL 和端口。

这个示例展示了基本的实时通信功能。你可以根据需要扩展这个示例,例如处理更多的事件、增加错误处理等。

回到顶部