Flutter视频会议插件jitsi_meet_flutter_sdk的使用

Flutter视频会议插件jitsi_meet_flutter_sdk的使用

Jitsi Meet Flutter SDK

License pub package

Jitsi Meet Flutter SDK 提供了与 Jitsi Meet 应用相同的用户体验,以 Flutter 插件的形式呈现,使您可以在自己的 Flutter 应用中嵌入和自定义 Jitsi Meet。

支持的平台

平台 支持 备注
Android ✅ 最低 API 级别为 24
iOS ✅ 最低支持版本为 13.4
Web ❌

示例应用

如果您想了解将 Jitsi Meet Flutter SDK 集成到 Flutter 应用程序是多么简单,请参考 示例应用程序仓库。

安装

添加依赖

通过命令行添加依赖:

$ flutter pub add jitsi_meet_flutter_sdk

上述命令会将以下内容添加到项目的 pubspec.yaml 文件中(您也可以手动添加):

dependencies:
    jitsi_meet_flutter_sdk: ^10.3.0

安装

从终端安装包:

$ flutter pub get

导入文件

在 Dart 代码中导入以下文件:

import 'package:jitsi_meet_flutter_sdk/jitsi_meet_flutter_sdk.dart';

使用方法

加入会议

首先创建一个 JitsiMeet 对象,然后调用它的 join 方法并传递一个 JitsiMeetConferenceOptions 对象。

var jitsiMeet = JitsiMeet();
var options = JitsiMeetConferenceOptions(room: 'jitsiIsAwesome');
jitsiMeet.join(options);

配置

iOS

确保在 ios 目录下的 Podfile 中设置 iOS 版本为 15.1 或更高:

platform :ios, '15.1'

该插件请求摄像头和麦克风访问权限,请确保在 ios/Runner 目录下的 Info.plist 文件中包含所需的 NSCameraUsageDescription 和 NSMicrophoneUsageDescription 条目:

<key>NSCameraUsageDescription</key>
<string>The app needs access to your camera for meetings.</string>
<key>NSMicrophoneUsageDescription</key>
<string>The app needs access to your microphone for meetings.</string>

要使用屏幕共享功能需要集成 Broadcast Upload Extension。请参阅:屏幕共享集成。

Android

前往 android/app/build.gradle 并确保 minSdkVersion 设置为 至少 24:

android {
    ...
    defaultConfig {
        ...
        minSdkVersion 24
    }
}

application:label 字段与 Jitsi Meet Android SDK 的冲突。前往 android/app/src/main/AndroidManifest.xml 并在 application 标签中添加 tools 库和 tools:replace="android:label":

<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:tools="http://schemas.android.com/tools">
    <application
        tools:replace="android:label"
        android:label="sample_app"
        android:name="${applicationName}"
        android:icon="@mipmap/ic_launcher">
        ...
    </application>
</manifest>

使用 API

JitsiMeet

JitsiMeet 类是 SDK 的入口点。它用于启动会议屏幕、发送和接收所有事件。

join(JitsiMeetConferenceOptions options, [JitsiMeetEventListener? listener])

加入具有给定选项的会议,并可选地提供一个监听器。

  • options: 会议选项。
  • listener: 由原生 SDK 触发的事件监听器。

hangUp()

本地参与者离开当前会议。

setAudioMuted(bool muted)

根据 muted 参数设置本地参与者的音频静音状态。

setVideoMuted(bool muted)

根据 muted 参数设置本地参与者的视频静音状态。

sendEndpointTextMessage({String? to, required String message})

通过数据通道向特定参与者或所有参与者发送消息。如果 to 参数为空,则消息将发送给会议中的所有参与者。

toggleScreenShare(bool enabled)

根据 enabled 参数设置本地参与者的屏幕共享状态。

openChat([String? to])

打开聊天对话框。如果 to 包含有效的 participantId,则将打开与该特定参与者的私人聊天。

sendChatMessage({String? to, required String message})

向特定参与者或所有参与者发送聊天消息。如果 to 参数为空,则消息将发送给会议中的所有参与者。

closeChat()

关闭聊天对话框。

retrieveParticipantsInfo()

发送触发 participantsInfoRetrieved 事件的消息,该事件包含参与者信息。

JitsiMeetConferenceOptions

此对象封装了加入会议时可以调整的所有选项。

var options = JitsiMeetConferenceOptions(
      serverURL: "https://meet.jit.si",
      room: "jitsiIsAwesomeWithFlutter",
      configOverrides: {
        "startWithAudioMuted": false,
        "startWithVideoMuted": false,
        "subject" : "Jitsi with Flutter",
      },
      featureFlags: {
        "unsaferoomwarning.enabled": false
      },
      userInfo: JitsiMeetUserInfo(
          displayName: "Flutter user",
          email: "user@example.com"
      ),
    );

JitsiMeetEventListener

此类旨在作为来自原生 SDK 事件的监听器。它将接收事件处理程序作为参数。

示例监听器

var listener = JitsiMeetEventListener(
      conferenceJoined: (url) {
        debugPrint("conferenceJoined: url: $url");
      },

      participantJoined: (email, name, role, participantId) {
        debugPrint(
          "participantJoined: email: $email, name: $name, role: $role, "
              "participantId: $participantId",
        );
        participants.add(participantId!);
      },

      chatMessageReceived: (senderId, message, isPrivate) {
        debugPrint(
          "chatMessageReceived: senderId: $senderId, message: $message, "
              "isPrivate: $isPrivate",
        );
      },

      readyToClose: () {
        debugPrint("readyToClose");
      },
    );

参考资料

在构建此项目时受到了 jitsi_meet_wrapper 的启发。

示例代码

以下是 example/lib/main.dart 中的完整示例代码:

import 'package:flutter/material.dart';

import 'package:jitsi_meet_flutter_sdk/jitsi_meet_flutter_sdk.dart';

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

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  bool audioMuted = true;
  bool videoMuted = true;
  bool screenShareOn = false;
  List<String> participants = [];
  final _jitsiMeetPlugin = JitsiMeet();

  join() async {
    var options = JitsiMeetConferenceOptions(
      room: "testgabigabi",
      configOverrides: {
        "startWithAudioMuted": true,
        "startWithVideoMuted": true,
      },
      featureFlags: {
        FeatureFlags.addPeopleEnabled: true,
        FeatureFlags.welcomePageEnabled: true,
        FeatureFlags.preJoinPageEnabled: true,
        FeatureFlags.unsafeRoomWarningEnabled: true,
        FeatureFlags.resolution: FeatureFlagVideoResolutions.resolution720p,
        FeatureFlags.audioFocusDisabled: true,
        FeatureFlags.audioMuteButtonEnabled: true,
        FeatureFlags.audioOnlyButtonEnabled: true,
        FeatureFlags.calenderEnabled: true,
        FeatureFlags.callIntegrationEnabled: true,
        FeatureFlags.carModeEnabled: true,
        FeatureFlags.closeCaptionsEnabled: true,
        FeatureFlags.conferenceTimerEnabled: true,
        FeatureFlags.chatEnabled: true,
        FeatureFlags.filmstripEnabled: true,
        FeatureFlags.fullScreenEnabled: true,
        FeatureFlags.helpButtonEnabled: true,
        FeatureFlags.inviteEnabled: true,
        FeatureFlags.androidScreenSharingEnabled: true,
        FeatureFlags.speakerStatsEnabled: true,
        FeatureFlags.kickOutEnabled: true,
        FeatureFlags.liveStreamingEnabled: true,
        FeatureFlags.lobbyModeEnabled: true,
        FeatureFlags.meetingNameEnabled: true,
        FeatureFlags.meetingPasswordEnabled: true,
        FeatureFlags.notificationEnabled: true,
        FeatureFlags.overflowMenuEnabled: true,
        FeatureFlags.pipEnabled: true,
        FeatureFlags.pipWhileScreenSharingEnabled: true,
        FeatureFlags.preJoinPageHideDisplayName: true,
        FeatureFlags.raiseHandEnabled: true,
        FeatureFlags.reactionsEnabled: true,
        FeatureFlags.recordingEnabled: true,
        FeatureFlags.replaceParticipant: true,
        FeatureFlags.securityOptionEnabled: true,
        FeatureFlags.serverUrlChangeEnabled: true,
        FeatureFlags.settingsEnabled: true,
        FeatureFlags.tileViewEnabled: true,
        FeatureFlags.videoMuteEnabled: true,
        FeatureFlags.videoShareEnabled: true,
        FeatureFlags.toolboxEnabled: true,
        FeatureFlags.iosRecordingEnabled: true,
        FeatureFlags.iosScreenSharingEnabled: true,
        FeatureFlags.toolboxAlwaysVisible: true,
      },
      userInfo: JitsiMeetUserInfo(
          displayName: "Gabi",
          email: "gabi.borlea.1@gmail.com",
          avatar:
              "https://avatars.githubusercontent.com/u/57035818?s=400&u=02572f10fe61bca6fc20426548f3920d53f79693&v=4"),
    );

    var listener = JitsiMeetEventListener(
      conferenceJoined: (url) {
        debugPrint("conferenceJoined: url: $url");
      },
      conferenceTerminated: (url, error) {
        debugPrint("conferenceTerminated: url: $url, error: $error");
      },
      conferenceWillJoin: (url) {
        debugPrint("conferenceWillJoin: url: $url");
      },
      participantJoined: (email, name, role, participantId) {
        debugPrint(
          "participantJoined: email: $email, name: $name, role: $role, "
          "participantId: $participantId",
        );
        participants.add(participantId!);
      },
      participantLeft: (participantId) {
        debugPrint("participantLeft: participantId: $participantId");
      },
      audioMutedChanged: (muted) {
        debugPrint("audioMutedChanged: isMuted: $muted");
      },
      videoMutedChanged: (muted) {
        debugPrint("videoMutedChanged: isMuted: $muted");
      },
      endpointTextMessageReceived: (senderId, message) {
        debugPrint(
            "endpointTextMessageReceived: senderId: $senderId, message: $message");
      },
      screenShareToggled: (participantId, sharing) {
        debugPrint(
          "screenShareToggled: participantId: $participantId, "
          "isSharing: $sharing",
        );
      },
      chatMessageReceived: (senderId, message, isPrivate, timestamp) {
        debugPrint(
          "chatMessageReceived: senderId: $senderId, message: $message, "
          "isPrivate: $isPrivate, timestamp: $timestamp",
        );
      },
      chatToggled: (isOpen) => debugPrint("chatToggled: isOpen: $isOpen"),
      participantsInfoRetrieved: (participantsInfo) {
        debugPrint(
            "participantsInfoRetrieved: participantsInfo: $participantsInfo, ");
      },
      readyToClose: () {
        debugPrint("readyToClose");
      },
    );
    await _jitsiMeetPlugin.join(options, listener);
  }

  hangUp() async {
    await _jitsiMeetPlugin.hangUp();
  }

  setAudioMuted(bool? muted) async {
    var a = await _jitsiMeetPlugin.setAudioMuted(muted!);
    debugPrint("$a");
    setState(() {
      audioMuted = muted;
    });
  }

  setVideoMuted(bool? muted) async {
    var a = await _jitsiMeetPlugin.setVideoMuted(muted!);
    debugPrint("$a");
    setState(() {
      videoMuted = muted;
    });
  }

  sendEndpointTextMessage() async {
    var a = await _jitsiMeetPlugin.sendEndpointTextMessage(message: "HEY");
    debugPrint("$a");

    for (var p in participants) {
      var b =
          await _jitsiMeetPlugin.sendEndpointTextMessage(to: p, message: "HEY");
      debugPrint("$b");
    }
  }

  toggleScreenShare(bool? enabled) async {
    await _jitsiMeetPlugin.toggleScreenShare(enabled!);

    setState(() {
      screenShareOn = enabled;
    });
  }

  openChat() async {
    await _jitsiMeetPlugin.openChat();
  }

  sendChatMessage() async {
    var a = await _jitsiMeetPlugin.sendChatMessage(message: "HEY1");
    debugPrint("$a");

    for (var p in participants) {
      a = await _jitsiMeetPlugin.sendChatMessage(to: p, message: "HEY2");
      debugPrint("$a");
    }
  }

  closeChat() async {
    await _jitsiMeetPlugin.closeChat();
  }

  retrieveParticipantsInfo() async {
    var a = await _jitsiMeetPlugin.retrieveParticipantsInfo();
    debugPrint("$a");
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
          appBar: AppBar(
            title: const Text('Plugin example app'),
          ),
          body: Center(
            child: Column(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: <Widget>[
                  TextButton(
                    onPressed: join,
                    child: const Text("Join"),
                  ),
                  TextButton(onPressed: hangUp, child: const Text("Hang Up")),
                  Row(children: [
                    const Text("Set Audio Muted"),
                    Checkbox(
                      value: audioMuted,
                      onChanged: setAudioMuted,
                    ),
                  ]),
                  Row(children: [
                    const Text("Set Video Muted"),
                    Checkbox(
                      value: videoMuted,
                      onChanged: setVideoMuted,
                    ),
                  ]),
                  TextButton(
                      onPressed: sendEndpointTextMessage,
                      child: const Text("Send Hey Endpoint Message To All")),
                  Row(children: [
                    const Text("Toggle Screen Share"),
                    Checkbox(
                      value: screenShareOn,
                      onChanged: toggleScreenShare,
                    ),
                  ]),
                  TextButton(
                      onPressed: openChat, child: const Text("Open Chat")),
                  TextButton(
                      onPressed: sendChatMessage,
                      child: const Text("Send Chat Message to All")),
                  TextButton(
                      onPressed: closeChat, child: const Text("Close Chat")),
                  TextButton(
                      onPressed: retrieveParticipantsInfo,
                      child: const Text("Retrieve Participants Info")),
                ]),
          )),
    );
  }
}

更多关于Flutter视频会议插件jitsi_meet_flutter_sdk的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter视频会议插件jitsi_meet_flutter_sdk的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何在Flutter项目中使用jitsi_meet_flutter_sdk插件来集成Jitsi Meet视频会议功能的代码示例。这个插件允许你在Flutter应用中嵌入Jitsi Meet视频会议界面。

1. 添加依赖

首先,在你的pubspec.yaml文件中添加jitsi_meet_flutter_sdk依赖:

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

2. 导入插件

在你需要使用Jitsi Meet功能的Dart文件中导入插件:

import 'package:jitsi_meet_flutter_sdk/jitsi_meet_flutter_sdk.dart';

3. 配置Jitsi Meet视图

接下来,你可以使用JitsiMeetView或JitsiMeetActivity(取决于你使用的是iOS还是Android,或者是否希望在Flutter内部处理UI)来启动视频会议。

示例:使用JitsiMeetView在Flutter中嵌入Jitsi Meet

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: JitsiMeetScreen(),
    );
  }
}

class JitsiMeetScreen extends StatefulWidget {
  @override
  _JitsiMeetScreenState createState() => _JitsiMeetScreenState();
}

class _JitsiMeetScreenState extends State<JitsiMeetScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Jitsi Meet Integration'),
      ),
      body: Center(
        child: JitsiMeetView(
          options: JitsiMeetViewOptions(
            room: 'https://meet.jit.si/YourMeetingRoom', // 替换为你的会议室URL
            userDisplayName: 'Flutter User',
            userInfo: <String, dynamic>{
              'email': 'user@example.com',
            },
            audioOnly: false,
            welcomePageEnabled: true,
            toolbarButtons: [
              JitsiMeetToolbarButton.hangup,
            ],
          ),
          listener: (JitsiMeetViewState state) {
            // 处理会议状态变化,例如会议结束等
            print('JitsiMeetView state: $state');
          },
          onConferenceTerminated: () {
            // 会议结束时回调
            print('Conference terminated');
          },
        ),
      ),
    );
  }
}

4. 配置Android权限(如果需要)

如果你的应用需要在Android设备上访问摄像头或麦克风,你需要在AndroidManifest.xml中添加相应的权限:

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

5. 运行应用

确保你已经连接了开发设备或启动了模拟器,然后在命令行中运行以下命令来构建并运行你的Flutter应用:

flutter run

注意事项

  • 确保你已经在Jitsi Meet服务器上配置了会议室。
  • 根据你的需求调整JitsiMeetViewOptions中的参数。
  • 在实际项目中,处理用户权限时要考虑用户体验和隐私保护。

这个示例展示了如何在Flutter应用中集成Jitsi Meet进行视频会议。根据你的具体需求,你可能需要进一步定制和扩展此功能。

回到顶部