Flutter WebSocket通信插件ya_websocket的使用

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

Flutter WebSocket通信插件ya_websocket的使用

ya_websocket

一个使用 iOS 和 Android 上原生库进行 WebSocket 通信的 Flutter 插件。

由于使用 Flutter 的 WebSocket 库尝试连接某台 IoT 设备通信时,总是无法连接,所以单独做了一个 WebSocket 库,它基于各系统中已经成熟的库进行通信。

感谢这些好用的库

平 台 Flutter 插件语言 原生端使用的库 库版本 库使用的语言
iOS Kotlin Java-WebSocket 1.5.2 Java
Android Swift Starscream 4.0.4 Swift

系统版本要求

平 台 可运行最低版本 推荐最低版本
Dart 2.12.0 2.13.4
Flutter 2 2.2.3
iOS 10 14
Android 4.1 11

在使用本插件前,请先将 APP 的适用版本设置为「可运行最低版本」之上。

引入

如果您准备使用 release 发布版本:

  1. 在项目目录中,运行 flutter pub add ya_websocket
  2. 运行后,您项目中 pubspec.yaml 中的 dependencies: 节会自动添加一行代码(并自动运行 dart pub get):
  ya_websocket: ^1.0.0 # 使用的版本号,你也可以修改它

如果您准备使用 main 主线版本,请添加:

  1. 编辑您项目中的文件 pubspec.yaml,在 dependencies: 节添加以下代码:
  ya_websocket:
    git:
      url: git://github.com/kagurazakayashi/yaWebSocket.git
      ref: main
  1. 保存后,在项目目录中运行 flutter pub get

使用

  1. 导入包: import 'package:ya_websocket/ya_websocket.dart';
  2. 在需要的类中实现接口 class ... implements YaWebsocketDelegate,实现以下接口:

已连接时

void yaWebsocketDelegateOnOpen(String httpStatus, String httpStatusMessage, String? tag);

参数:

  • httpStatus: HTTP 状态码
  • httpStatusMessage: 状态描述文本
  • tag: 自定义标记

开始尝试连接时

void yaWebsocketDelegateOnConnecting(String? tag);

参数:

  • tag: 自定义标记

收到信息时

void yaWebsocketDelegateOnMessage(String message, String? tag);

参数:

  • message: 信息内容
  • tag: 自定义标记

连接关闭时 (Android 和 iOS 返回信息可能有区别)

void yaWebsocketDelegateOnClose(String code, String reason, String remote, String? tag);

参数:

  • code: 状态代码
  • reason: 描述文本
  • remote: 是否由远程关闭
  • tag: 自定义标记

连接发生错误时 (Android 和 iOS 返回信息可能有区别)

void yaWebsocketDelegateOnError(String localizedMessage, String? message, String? tag);

参数:

  • localizedMessage: 本地化描述文本
  • message: 描述文本
  • tag: 自定义标记

通常情况下的状态代码( code )

  • -1 表示操作失败/不正常
  • 0 表示正常/正常地处于关闭状态
  • 1 表示正常/正常地处于开启状态
  1. 创建对象: YaWebsocket websocket = YaWebsocket();
  2. 指定接口实现类: _websocket.delegate = this;
  3. 开始连接: websocket.connect(uri, tag: tag);

参数:

  • uri: Websocket 的连接地址,以 ws:// 开头。
  • tag: 可选标签,可以输入任意字符串,库调用接口返回时,会带上它。

返回值(字符串字典):

  • status:
    • 0: 现在开始连接,请等待接口收到 yaWebsocketDelegateOnOpenyaWebsocketDelegateOnClose 调用后再进行下一步。
    • -1: 未能开始连接
  • info: (可选)如果未能开始连接,可能提供此错误信息描述
  1. 检查是否连接 websocket.isOpen();

返回值(字符串字典):

  • status:
    • 1: 已连接
    • 0: 未连接
    • -1: 未能查询
  • info: (可选)如果未能查询,可能提供此错误信息描述
  1. 发送数据: websocket.send(text);

参数:

  • text: 要发送的字符串

返回值(字符串字典):

  • status:
    • 0: 已发送
    • -1: 未发送
  • info: (可选)如果未能发送,可能提供此错误信息描述
  1. 断开连接: websocket.close();

返回值(字符串字典):

  • status:
    • 0: 关闭成功或已经处于关闭状态,关闭成功接口将收到 yaWebsocketDelegateOnClose 调用。
    • -1: 遇到问题
  • info: (可选)如果遇到问题,可能提供此错误信息描述

注意事项

  1. 执行 websocket.* 时会立即有返回值,该返回值通常只表示是否成功开始执行。真正的运行结果状态回调在 YaWebsocketDelegate
  2. 正在进行操作时(如正在连接、正在断开链接时)不要进行操作,尤其是重复的连接和断开操作,请等到收到状态回调后再进行下一步操作。
  3. 连接关闭的回调 yaWebsocketDelegateOnClose 会在每次连接关闭时触发,而错误回调 yaWebsocketDelegateOnError 仅在识别到错误时触发。如果因错误断开,yaWebsocketDelegateOnErroryaWebsocketDelegateOnClose 都会触发。因此建议在 yaWebsocketDelegateOnClose 中判断连接关闭。

示例程序

DEMO 演示了 连接、发送、接收、断开、重新连接 的过程。

Screenshot
  • 在输入框中输入 ws:// 地址进行连接。
  • 然后输入内容并发送,屏幕上会以聊天式气泡显示接收和发送的信息。
  • 点按右上角连接图标可以连接和断开,请在当前连接或断开操作完成或超时后再按。

<iOS 示例程序> | <Android 示例程序>

更多

dartdoc | 更新日志

许可


示例代码

import 'dart:async';

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

import 'package:ya_websocket/ya_websocket.dart';

import 'package:bubble/bubble.dart';

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

class MyApp extends StatefulWidget {
  [@override](/user/override)
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> implements YaWebsocketDelegate {
  String _webSocketURI = ''; // ws://192.168.1.46:8866/chat?user_id=1
  String _webSocketTAG = '0';
  late TextEditingController _textFController;
  List _data = [];
  bool _isConnect = false;
  late YaWebsocket _websocket;
  String _title = "点按屏幕下方橙色区域输入";
  ScrollController _scrollController = ScrollController();

  [@override](/user/override)
  void initState() {
    initPlatformState().whenComplete(() {});
    _textFController = TextEditingController(text: '');
    _websocket = YaWebsocket();
    _websocket.delegate = this;
    if (_webSocketURI.length > 0) {
      connect(_webSocketURI, _webSocketTAG);
    } else {
      setState(() {
        _textFController.text = "ws://";
        _data.add([false, "请输入连接地址,以 ws:// 开头。"]);
      });
    }
    super.initState();
  }

  [@override](/user/override)
  void dispose() {
    _textFController.dispose();
    _websocket.close();
    super.dispose();
  }

  Future<void> initPlatformState() async {
    // String platformVersion;
    // try {
    //   platformVersion =
    //       await websocket.platformVersion ?? 'Unknown platform version';
    // } on PlatformException {
    //   platformVersion = 'Failed to get platform version.';
    // }
    // if (!mounted) return;
    // setState(() {
    //   _platformVersion = platformVersion;
    //   // child: Text('Running on: $_platformVersion\n'),
    // });
  }

  Future<bool> send(String text) async {
    bool isOK = true;
    Map? info = await _websocket.send(text);
    if (info!["status"] == "-1") {
      setState(() {
        _data.add(
            [false, "发送失败 " + (info.containsKey("info") ? info["info"] : "")]);
      });
      isOK = false;
    }
    return isOK;
  }

  Future<bool> close() async {
    bool isOK = true;
    setState(() {
      _data.add([false, "正在断开..."]);
    });
    Map? info = await _websocket.close();
    if (info!["status"] == "-1") {
      setState(() {
        _data.add([
          false,
          "断开时出现异常 " + (info.containsKey("info") ? info["info"] : "")
        ]);
      });
      isOK = false;
    }
    return isOK;
  }

  Future<bool> reconnect() async {
    bool isOK = true;
    setState(() {
      _data.add([false, "正在尝试连接到上次的地址..."]);
    });
    Map? info = await _websocket.reconnect();
    if (info!["status"] == "-1") {
      setState(() {
        _data.add(
            [false, "未能连接 " + (info.containsKey("info") ? info["info"] : "")]);
      });
      isOK = false;
    }
    return isOK;
  }

  Future<bool> connect(String toURI, String toTAG) async {
    bool isOK = true;
    setState(() {
      _title = toURI;
      _data.add([false, "现在开始尝试连接 $toURI ..."]);
    });
    Map? info = await _websocket.connect(toURI, tag: toTAG);
    if (info!["status"] == "-1") {
      setState(() {
        _data.add(
            [false, "未能连接 " + (info.containsKey("info") ? info["info"] : "")]);
      });
      isOK = false;
    }
    return isOK;
  }

  [@override](/user/override)
  yaWebsocketDelegateOnClose(
      String code, String reason, String remote, String? tag) {
    setState(() {
      _isConnect = false;
      _data.add([
        false,
        "连接已被关闭( $remote , $code ) $reason ,请输入新的连接地址或按右上角按钮重新连接到上次的服务器。"
      ]);
    });
  }

  [@override](/user/override)
  yaWebsocketDelegateOnError(
      String localizedMessage, String? message, String? tag) {
    setState(() {
      _data.add([false, "发生错误: $localizedMessage ."]);
    });
  }

  [@override](/user/override)
  yaWebsocketDelegateOnMessage(String message, String? tag) {
    setState(() {
      _data.add([false, message]);
    });
  }

  [@override](/user/override)
  yaWebsocketDelegateOnOpen(
      String httpStatus, String httpStatusMessage, String? tag) {
    setState(() {
      _isConnect = true;
      _data
          .add([false, "连接已建立( $httpStatus ): $httpStatusMessage 。请输入要发送的消息。"]);
    });
  }

  [@override](/user/override)
  yaWebsocketDelegateOnConnecting(String? tag) {
    setState(() {
      _data.add([false, "正在连接 $tag ($_webSocketURI) ,请不要进行其他操作。"]);
    });
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    if (_data.length > 0 &&
        _scrollController.hasClients &&
        _scrollController.position.maxScrollExtent > _scrollController.offset) {
      Timer(
        Duration(milliseconds: 500),
        () => _scrollController.animateTo(
          _scrollController.position.maxScrollExtent,
          duration: Duration(milliseconds: 200),
          curve: Curves.ease,
        ),
      );
    }
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text(_title),
          actions: [
            IconButton(
              onPressed: () async {
                if (await _websocket.isOpen) {
                  close();
                } else {
                  reconnect();
                }
              },
              icon: Icon(
                _isConnect ? Icons.link_off : Icons.link,
              ),
            ),
          ],
        ),
        body: Column(
          children: [
            Expanded(
              child: ListView.builder(
                controller: _scrollController,
                padding: const EdgeInsets.all(16.0),
                itemCount: _data.length, //data.length*2-1,
                itemBuilder: (context, i) {
                  return Container(
                    child: InkWell(
                      onLongPress: () {
                        _textFController.text = _data[i][1].toString();
                      },
                      child: Bubble(
                        margin: BubbleEdges.only(top: 10),
                        alignment: _data[i][0]
                            ? Alignment.centerRight
                            : Alignment.centerLeft,
                        nip: _data[i][0]
                            ? BubbleNip.rightBottom
                            : BubbleNip.leftBottom,
                        color: _data[i][0]
                            ? Color.fromRGBO(225, 255, 199, 1.0)
                            : Color.fromRGBO(212, 234, 244, 1.0),
                        child: Text(
                          _data[i][1],
                          textAlign:
                              _data[i][0] ? TextAlign.right : TextAlign.left,
                        ),
                      ),
                    ),
                  );
                },
              ),
            ),
            Container(
              height: 50,
              color: Colors.deepOrange,
              child: Row(
                children: [
                  Expanded(
                    child: TextField(
                      controller: _textFController,
                      onSubmitted: (value) {
                        setState(() {
                          _data.add([true, value]);
                        });
                        if (value.length >= 5 && value.substring(0, 5) == "ws://") {
                          connect(value, _webSocketTAG);
                        } else {
                          send(value);
                        }
                        _textFController.text = "";
                      },
                    ),
                  ),
                  IconButton(
                    onPressed: () {
                      _textFController.text = '';
                    },
                    icon: Icon(Icons.clear, color: Colors.white),
                  ),
                  IconButton(
                    onPressed: () {
                      String value = _textFController.text;
                      setState(() {
                        _data.add([true, value]);
                      });
                      if (value.length >= 5 && value.substring(0, 5) == "ws://") {
                        connect(value, _webSocketTAG);
                      } else {
                        send(value);
                      }
                      _textFController.text = "";
                    },
                    icon: Icon(
                      Icons.send,
                      color: Colors.white,
                    ),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

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

1 回复

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


当然,以下是一个关于如何在Flutter项目中使用ya_websocket插件进行WebSocket通信的代码示例。这个示例将展示如何建立WebSocket连接、发送消息以及接收消息。

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

dependencies:
  flutter:
    sdk: flutter
  ya_websocket: ^x.y.z  # 请替换为最新版本号

然后,运行flutter pub get来安装依赖。

接下来,我们编写Flutter代码来实现WebSocket通信。

main.dart

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

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

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

class WebSocketPage extends StatefulWidget {
  @override
  _WebSocketPageState createState() => _WebSocketPageState();
}

class _WebSocketPageState extends State<WebSocketPage> {
  WebSocketClient? _webSocketClient;
  TextEditingController _messageController = TextEditingController();
  List<String> _messages = [];

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

  void initWebSocket() {
    String url = 'wss://your-websocket-server-url'; // 替换为你的WebSocket服务器URL
    _webSocketClient = WebSocketClient(url);

    _webSocketClient!.onMessage!.listen((WebSocketMessage message) {
      setState(() {
        _messages.add('Received: ${message.data}');
      });
    });

    _webSocketClient!.onOpen!.listen((_) {
      setState(() {
        _messages.add('WebSocket connected.');
      });
    });

    _webSocketClient!.onClose!.listen((_) {
      setState(() {
        _messages.add('WebSocket disconnected.');
      });
    });

    _webSocketClient!.onError!.listen((error) {
      setState(() {
        _messages.add('Error: $error');
      });
    });

    _webSocketClient!.connect();
  }

  void sendMessage() {
    if (_webSocketClient!.readyState == WebSocketReadyState.OPEN) {
      String message = _messageController.text;
      _webSocketClient!.send(message);
      setState(() {
        _messages.add('Sent: $message');
        _messageController.clear();
      });
    } else {
      setState(() {
        _messages.add('WebSocket is not open.');
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('WebSocket Demo'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Column(
          children: <Widget>[
            Expanded(
              child: ListView.builder(
                itemCount: _messages.length,
                itemBuilder: (context, index) {
                  return ListTile(
                    title: Text(_messages[index]),
                  );
                },
              ),
            ),
            TextField(
              controller: _messageController,
              decoration: InputDecoration(
                border: OutlineInputBorder(),
                labelText: 'Message',
              ),
              onSubmitted: (String value) {
                sendMessage();
              },
            ),
            ElevatedButton(
              onPressed: sendMessage,
              child: Text('Send'),
            ),
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    _webSocketClient?.close();
    _messageController.dispose();
    super.dispose();
  }
}

解释

  1. 依赖导入

    • 导入ya_websocket包和其他必要的Flutter包。
  2. 主应用

    • MyApp是根组件,它定义了应用的主题和主页。
  3. WebSocket页面

    • WebSocketPage是一个有状态的组件,它管理WebSocket连接和消息。
  4. 初始化WebSocket

    • initState方法中,创建WebSocketClient实例,并设置消息、打开、关闭和错误事件的监听器。
    • 调用connect方法启动WebSocket连接。
  5. 发送消息

    • sendMessage方法检查WebSocket是否打开,如果打开则发送消息,并更新消息列表。
  6. UI构建

    • 使用ListView.builder显示消息列表。
    • 使用TextFieldElevatedButton构建消息输入框和发送按钮。
  7. 资源释放

    • dispose方法中关闭WebSocket连接并释放控制器。

请确保将wss://your-websocket-server-url替换为你的实际WebSocket服务器URL。这个示例展示了如何使用ya_websocket插件在Flutter中实现基本的WebSocket通信功能。

回到顶部