Flutter即时通讯插件xmpp_client_web的使用

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

Flutter即时通讯插件xmpp_client_web的使用

轻量级的XMPP客户端库,完全用Dart编写。我的目的是为未来的基于Flutter的XMPP客户端编写一个易于使用的库。

支持的文档:

  • RFC6120:可扩展的消息与存在协议(XMPP):核心
  • RFC6121:可扩展的消息与存在协议(XMPP):即时消息与存在
  • XEP-0198:流管理
  • XEP-0085:聊天状态通知
  • XEP-0318:客户端发起的存在探测最佳实践
  • XEP-0280:消息碳副本

部分支持:

  • XEP-0030:服务发现
  • XEP-0313:消息归档管理

积极开发中:

  • XEP-0059:结果集管理
  • XEP-0004:数据表单

最新动态

  • 2021-12-14:添加了对XEP-0016的支持:隐私列表
  • 2020-10-30:添加了对XEP-0280的支持:消息碳副本
  • 2020-10-30:初步支持XEP-0313:消息归档管理
  • 2020-10-30:添加了日志机制
  • 2020-07-23:添加了对XEP-0318的支持:客户端发起的存在探测最佳实践
  • 2020-05-02:添加了对XEP-0198的初步支持:流管理
  • 2020-05-02:添加了对XEP-0085的初步支持:聊天状态通知
  • 2019-04-02:添加了对XEP-0054的支持:vcard-temp
  • 2019-04-01:添加了SHA-1和SHA-256认证算法的支持

使用方法

import 'package:xmpp_client_web/xmpp_stone.dart' as xmpp;

void main() {
  xmpp.XmppAccountSettings accountSettings = xmpp.XmppAccountSettings.fromJid('nick@damain.com/resource', 'password');
  accountSettings.port = kIsWeb ? 5291 : 5222;
  accountSettings.wsPath = 'xmpp-websocket'; // null 或者自定义的 xmpp 路径
  accountSettings.wsProtocols = ['xmpp']; // 或者自定义的协议

  xmpp.Connection connection = xmpp.Connection.getInstance(accountSettings);
  connection.open();
}

功能特性

1. 日志记录

日志级别

客户端可以设置库的日志级别:

Log.logLevel = LogLevel.VERBOSE;

XMPP流量日志

可以启用或禁用XMPP流量日志:

Log.logXmpp = false;

2. 消息归档管理

此功能仍在开发中,API可能会发生变化。

模块获取

connection.getMamModule()

查询所有消息

mamManager.queryAll();

基于日期查询消息(所有参数都是可选的)

mamManager.queryByTime(start: startTime, end: endTime, jid: buddyJid);

基于消息ID查询消息(所有参数都是可选的)

mamManager.queryById(beforeId: beforeId, afterId: afterId, jid: buddyJid});

检查服务器功能

mamManager.isQueryByDateSupported
mamManager.isQueryByIdSupported
mamManager.isQueryByJidSupported

完整示例代码

以下是完整的示例代码,演示了如何使用 xmpp_client_web 插件进行基本的即时通讯。

import 'dart:async';
import 'package:xmpp_client_web/src/logger/Log.dart';
import 'package:xmpp_client_web/xmpp_stone.dart' as xmpp;

final String TAG = 'example';

void main(List<String> arguments) async {
  Log.logLevel = LogLevel.DEBUG;
  Log.logXmpp = false;

  Log.d(TAG, 'Setting up XMPP connection...');
  var userAtDomain = 'nemanja@127.0.0.1';
  var password = '1';
  var jid = xmpp.Jid.fromFullJid(userAtDomain);
  var account = xmpp.XmppAccountSettings(
      userAtDomain, jid.local, jid.domain, password, 5222,
      resource: 'xmppstone');

  var connection = xmpp.Connection(account);

  // 注册监听器
  connection.connectionStateStream.listen((state) {
    Log.d(TAG, 'Connection state: $state');
    if (state == xmpp.XmppConnectionState.ForcefullyClosed) {
      Log.d(TAG, 'Connection lost. Attempting to reconnect...');
      reconnect(connection);
    }
  });

  var messagesListener = ExampleMessagesListener();
  ExampleConnectionStateChangedListener(connection, messagesListener);

  var presenceManager = xmpp.PresenceManager.getInstance(connection);
  presenceManager.subscriptionStream.listen((streamEvent) {
    if (streamEvent.type == xmpp.SubscriptionEventType.REQUEST) {
      Log.d(TAG, 'Accepting presence request');
      presenceManager.acceptSubscription(streamEvent.jid);
    }
  });

  var receiver = 'nemanja2[@test](/user/test)';
  var receiverJid = xmpp.Jid.fromFullJid(receiver);
  var messageHandler = xmpp.MessageHandler.getInstance(connection);

  // 等待连接就绪
  await waitForConnectionReady(connection);

  // 发送消息
  sendMessageSafely(messageHandler, receiverJid, 'Hello from xmpp_stone!');

  // 连接
  connection.connect();
}

// 等待连接就绪
Future<void> waitForConnectionReady(xmpp.Connection connection) async {
  while (connection.state != xmpp.XmppConnectionState.Ready) {
    await Future.delayed(const Duration(milliseconds: 500));
  }
}

// 重新连接
void reconnect(xmpp.Connection connection) async {
  Log.d(TAG, 'Attempting to reconnect...');
  await Future.delayed(const Duration(seconds: 2));
  connection.connect();
}

// 安全发送消息
void sendMessageSafely(xmpp.MessageHandler handler, xmpp.Jid receiverJid, String message) {
  try {
    handler.sendMessage(receiverJid, message);
    Log.d(TAG, 'Message sent: $message');
  } catch (e) {
    Log.d(TAG, 'Failed to send message: $e');
  }
}

class ExampleConnectionStateChangedListener
    implements xmpp.ConnectionStateChangedListener {
  late xmpp.Connection _connection;
  late xmpp.MessagesListener _messagesListener;

  ExampleConnectionStateChangedListener(
      xmpp.Connection connection, xmpp.MessagesListener messagesListener) {
    _connection = connection;
    _messagesListener = messagesListener;
    _connection.connectionStateStream.listen(onConnectionStateChanged);
  }

  [@override](/user/override)
  void onConnectionStateChanged(xmpp.XmppConnectionState state) {
    if (state == xmpp.XmppConnectionState.Ready) {
      Log.d(TAG, 'Connected');
      setupXMPPFeatures();
    }
  }

  Future<void> setupXMPPFeatures() async {
    var vCardManager = xmpp.VCardManager(_connection);
    var messageHandler = xmpp.MessageHandler.getInstance(_connection);
    var rosterManager = xmpp.RosterManager.getInstance(_connection);

    // 获取自己的VCard
    var vCard = await vCardManager.getSelfVCard();
    Log.d(TAG, 'Your info: ${vCard.buildXmlString()}');

    // 添加联系人到通讯录
    var receiver = 'nemanja2[@test](/user/test)';
    var receiverJid = xmpp.Jid.fromFullJid(receiver);
    var addResult = await rosterManager.addRosterItem(xmpp.Buddy(receiverJid));
    if (addResult.description != null) {
      Log.d(TAG, 'Roster added: ${addResult.description}');
    }

    // 获取联系人的VCard
    var receiverVCard = await vCardManager.getVCardFor(receiverJid);
    Log.d(TAG, 'Receiver info: ${receiverVCard.buildXmlString()}');

    // 启动存在状态监听器
    var presenceManager = xmpp.PresenceManager.getInstance(_connection);
    presenceManager.presenceStream.listen(onPresence);

    // 启动消息监听器
    messageHandler.messagesStream.listen(_messagesListener.onNewMessage);
  }

  void onPresence(xmpp.PresenceData event) {
    Log.d(TAG, 'Presence event from ${event.jid!.fullJid!}: ${event.showElement}');
  }
}

class ExampleMessagesListener implements xmpp.MessagesListener {
  [@override](/user/override)
  void onNewMessage(xmpp.MessageStanza? message) {
    if (message?.body != null) {
      Log.d(TAG, 'New Message from ${message!.fromJid!.userAtDomain}: ${message.body}');
    }
  }
}

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

1 回复

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


当然,下面是一个关于如何在Flutter项目中使用xmpp_client_web插件来实现即时通讯的示例代码。这个示例将展示如何连接到XMPP服务器、认证用户以及发送和接收消息。

首先,确保你的Flutter项目已经添加了xmpp_client_web依赖。在pubspec.yaml文件中添加以下依赖:

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

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

接下来是示例代码:

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

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

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  XmppClient? client;
  String? status;
  String? messageContent;

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

  Future<void> connectAndAuthenticate() async {
    // 配置XMPP客户端
    var options = XmppClientOptions(
      jid:Jid('your-username@your-xmpp-server'),
      password: 'your-password',
      server: 'your-xmpp-server',
      port: 5222,
      useTls: true,
    );

    client = XmppClient(options);

    // 监听连接状态
    client!.connectionStatus!.listen((event) {
      setState(() {
        status = 'Connection Status: $event';
      });
      if (event == XmppConnectionStatus.connected) {
        // 连接成功后发送在线状态
        client!.sendPresence();
      }
    });

    // 监听消息
    client!.messageReceived!.listen((message) {
      setState(() {
        messageContent = message.body;
      });
    });

    // 连接到服务器并认证
    await client!.connect();
  }

  Future<void> sendMessage(String to, String body) async {
    if (client!.isConnected!) {
      var message = Message(
        to: Jid(to),
        body: body,
      );
      await client!.sendMessage(message);
    } else {
      print('Client is not connected');
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('XMPP Client Demo'),
        ),
        body: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(status ?? 'Connecting...'),
            SizedBox(height: 20),
            TextField(
              decoration: InputDecoration(labelText: 'To'),
              onChanged: (value) async {
                // 这里可以添加逻辑来选择或输入接收者JID
              },
            ),
            SizedBox(height: 10),
            TextField(
              decoration: InputDecoration(labelText: 'Message'),
              onChanged: (value) {
                // 输入消息内容
              },
              onEditingComplete: () async {
                // 当用户完成输入时发送消息
                await sendMessage('receiver-jid@your-xmpp-server', messageContent ?? '');
              },
            ),
            SizedBox(height: 20),
            Text(messageContent ?? 'No message received yet'),
          ],
        ),
      ),
    );
  }
}

注意事项:

  1. 依赖版本:确保xmpp_client_web的版本是最新的,或者根据官方文档选择一个兼容的版本。
  2. JID格式:XMPP中的JID(Jabber Identifier)通常是username@domain格式。确保你使用的JID和服务器地址是正确的。
  3. 安全性:在实际应用中,不要在客户端代码中硬编码密码。考虑使用更安全的方法,如OAuth或其他认证机制。
  4. 错误处理:示例代码中没有包含详细的错误处理逻辑。在实际应用中,应该添加适当的错误处理来应对网络问题、认证失败等情况。
  5. UI优化:示例UI非常基础,你可以根据需要进行优化和美化。

这个示例展示了如何使用xmpp_client_web插件进行基本的XMPP通信。根据你的需求,你可能需要扩展这个示例以支持更多功能,如群聊、文件传输等。

回到顶部