Flutter视频会议插件jitsi_meet_flutter_sdk的使用

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

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 文件中包含所需的 NSCameraUsageDescriptionNSMicrophoneUsageDescription 条目:

<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视图

接下来,你可以使用JitsiMeetViewJitsiMeetActivity(取决于你使用的是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进行视频会议。根据你的具体需求,你可能需要进一步定制和扩展此功能。

回到顶部