Flutter会议安排插件meet_hour的使用

Flutter会议安排插件meet_hour的使用

Meet Hour SDK for Flutter。支持Web、Android和iOS平台。

“Meet Hour是一个高清质量视频会议解决方案,并且具有端到端加密等多项功能,例如大厅模式、直播流媒体连接用于筹款活动、视频通话录制、YouTube直播流等。” 请注册开发者或以上计划以使用此SDK。访问 https://meethour.io 获取更多详情。

支持Web、Android & iOS

示例项目

Pub.dev

  meet_hour: '>=5.0.24'

MeetHour SDK 实施 - 注册开发者或以上计划

  1. SDK 示例链接: https://github.com/v-empower/MeetHour-Web-MobileSDKs
  2. API 文档链接: https://docs.v-empower.com/docs/MeetHour-API/

目录

支持的API端点

  1. 获取访问令牌端点:

    LoginType LoginObject = LoginType(
      client_id: '',
      client_secret: '',
      grant_type: 'password',
      password: '',
      username: ''
      );
    
     Map<String, dynamic> response = await ApiServices.login(LoginObject);
    

    需要传递相应的值来获取所需响应。

  2. 安排会议:

    ScheduleMeetingType ScheduleObject = ScheduleMeetingType(
      meetingName : 'Test',
      agenda: '',
      passcode: '',
      meetingDate: '',
      meetingTime: '',
      meetingMeridiem: '',
      durationhr: '',
      durationmin: '',
      timezone: '',
      isrecurring: '',
      recurringtype: '',
      repeat_interval: '',
      endBy: '',
      enddatetime: '',
      instructions: '',
      is_show_portal: '',
      enablepreregistration: '',
      meetingtopic: '',
      meetingagenda: '',
      options: '',
      attend: '',
      groups: '',
      hostusers: ''
    );
    
    Map<String, dynamic> response = await ApiServices.scheduleMeeting(token, ScheduleObject);
    
  3. 生成JWT令牌端点:

    GenerateJwtType JWTObject = GenerateJwtType(
      config: '',
      contactid: '',
      meetingid: '',
      uiconfig: ''
    );
    
    Map<String, dynamic> response = await ApiServices.generateJwt(token, JWTObject);
    
  4. 获取用户详细信息:

    Map<String, dynamic> response = await ApiServices.userDetails(token);
    
  5. 使用刷新令牌获取访问令牌:

    RefreshTokenType RefreshTokenObject = RefreshTokenType(
             client_id: '',
             client_secret: '',
             grant_type: '',
             refresh_token: ''
    )
    
    Map<String, dynamic> response = await ApiServices.getRefreshToken(token, RefreshTokenObject);
    
  6. 将联系人添加到Meet Hour数据库:

    AddContactType AddContactObject = AddContactType(
          countrycode: '',
          email: '',
          firstname: '',
          image: '',
          is_show_portal: '',
          lastname: '',
          phone: ''
     );
    
    Map<String, dynamic> response = await ApiServices.addContact(token, AddContactObject);
    
  7. 获取各国时区列表:

    Map<String, dynamic> response = await ApiServices.timezone(token);
    
  8. 获取Meet Hour帐户中所有联系人列表:

    ContactsType AddContactObject = ContactsType(
      exclude_hosts: 1100,
      limit: 10,
      page: 1
    );
    
    Map<String, dynamic> response = await ApiServices.contactsList(token, AddContactObject);
    
  9. 更改现有联系人的详细信息:

    EditContactType EditContactObject = EditContactType(
      contactid: 1150;
      countrycode: '',
      email: '',
      firstname: '',
      image: '',
      is_show_portal: '',
      lastname: '',
      phone: ''
    );
    
    Map<String, dynamic> response = await ApiServices.editContact(token, EditContactObject);
    
  10. 获取即将召开的会议:

    UpcomingMeetingType UpcomingMeetingObject = UpcomingMeetingType(
      page: 10,
      limit: 10,
      show_all: 1
    );
    
    Map<String, dynamic> response = await ApiServices.upcomingMeetings(token, UpcomingMeetingObject);
    
  11. 存档会议:

    ArchiveMeetingType ArchiveMeetingObject = ArchiveMeetingType(
      id: 10
    );
    
    Map<String, dynamic> response = await ApiServices.archiveMeeting(token, ArchiveMeetingObject);
    
  12. 获取错过会议的详细信息:

    MissedMeetingType MissedMeetingObject = MissedMeetingType(
      limit: 10,
      page: number,
      show_all: 1
    );
    
    Map<String, dynamic> response = await ApiServices.missedMeetings(token, MissedMeetingObject);
    
  13. 获取已完成的会议:

    CompletedMeetingType CompletedMeetingObject = CompletedMeetingType(
      limit: 10,
      page: number,
      show_all: 1
    );
    
    Map<String, dynamic> response = await ApiServices.completedMeetings(token, CompletedMeetingObject);
    
  14. 编辑现有会议:

    EditMeetingType EdiMeetingObject = EditMeetingType(
      meeting_id: '',
      meetingName : 'Test',
      agenda: '',
      passcode: '',
      meetingDate: '',
      meetingTime: '',
      meetingMeridiem: '',
      durationhr: '',
      durationmin: '',
      timezone: '',
      isrecurring: '',
      recurringtype: '',
      repeat_interval: '',
      endBy: '',
      enddatetime: '',
      instructions: '',
      is_show_portal: '',
      enablepreregistration: '',
      meetingtopic: '',
      meetingagenda: '',
      old_attend: [],
      options: '',
      attend: '',
      groups: '',
      hostusers: ''
    );
    
    Map<String, dynamic> response = await ApiServices.editMeeting(token, EdiMeetingObject);
    
  15. 查看会议:

    ViewMeetingType ViewMeetingObject = ViewMeetingType(
      meeting_id: ''
    );
    
    Map<String, dynamic> response = await ApiServices.viewMeeting(token, ViewMeetingObject);
    
  16. 获取所有录制列表:

    RecordingsListType RecordingMeetingObject = RecordingsListType(
      filter_by: '',
      limit: '',
      page: ''
    );
    
    Map<String, dynamic> response = await ApiServices.recordingsList(token, RecordingMeetingObject);
    

Web 配置

该插件允许你在Flutter Web项目中插入Meet Hour会议。

  • 设置会议视图作为Flutter组件的子组件,使用onload回调调整会议部分大小。
  • 设置会议事件监听器。
  • 发送命令到会议。

为了实现这一点,你需要通过传递在以下链接中可用的API密钥包含Meet Hour外部API:https://portal.meethour.io/customer/developers

<script src="https://api.meethour.io/libs/v2.4.6/external_api.min.js?apiKey=${API_KEY}" type="application/javascript"></script>

示例:

<body>
  <script>
    if ('serviceWorker' in navigator) {
      window.addEventListener('load', function () {
        navigator.serviceWorker.register('/flutter_service_worker.js');
      });
    }
  </script>
  <script src="https://api.meethour.io/libs/v2.4.6/external_api.min.js?apiKey=${API_KEY}" type="application/javascript"></script>
  <script src="main.dart.js" type="application/javascript"></script>
</body>
</html>

Android & iOS 配置

IOS

注意:示例在XCode 12.2 & Flutter 1.22.4下可编译。

Podfile

确保你的Podfile中有如下条目,声明平台为11.0或更高版本,并禁用BITCODE。

platform :ios, '12.1'

...

post_install do |installer|
  installer.pods_project.targets.each do |target|
  flutter_additional_ios_build_settings(target)
      target.build_configurations.each do |config|
        config.build_settings['ENABLE_BITCODE'] = 'NO'
        config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64"
         end
     end
   end
Info.plist

在Info.plist中添加NSCameraUsageDescription和NSMicrophoneUsageDescription。

<key>NSCameraUsageDescription</key>
<string>$(PRODUCT_NAME) MyApp需要访问您的相机进行会议。</string>
<key>NSMicrophoneUsageDescription</key>
<string>$(PRODUCT_NAME) MyApp需要访问您的麦克风进行会议。</string>

Android

Gradle

将构建工具gradle的依赖项设置为最低3.6.3:

dependencies {
    classpath 'com.android.tools.build:gradle:7.1.0' <!-- 升级此 -->
    classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}

将gradle分发设置为最低5.6.4。

distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip <!-- 升级此 -->
AndroidManifest.xml

Meet Hour的SDK AndroidManifest.xml会与你的项目冲突,特别是application:label字段。为了解决这个问题,进入android/app/src/main/AndroidManifest.xml,并添加工具库和tools:replace="android:label"到应用标签。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="yourpackage.com"
    xmlns:tools="http://schemas.android.com/tools">
    <application 
        tools:replace="android:label"  
        android:name="your.application.name"
        android:label="My Application"
        android:icon="@mipmap/ic_launcher">
        ...
    </application>
...
</manifest>
最低SDK版本23

android/app/build.gradle中更新你的最低SDK版本至23。

defaultConfig {
    applicationId "go.meethour.io.flutter.sdk_example"
    minSdkVersion 23 //Required for MeetHour
    targetSdkVersion 33
    versionCode flutterVersionCode.toInteger()
    versionName flutterVersionName
}
Proguard

MeetHour的SDK启用了Proguard,但如果没有一个proguard-rules.pro文件,你的发布APK构建将会缺少Flutter Wrapper以及react-native代码。在你的Flutter项目的android/app/build.gradle文件中添加Proguard支持:

buildTypes {
    release {
        // TODO: 添加自己的签名配置以进行发布构建。
        // 目前使用调试密钥,以便`flutter run --release`可以工作。
        signingConfig signingConfigs.debug

        // 添加以下3行以启用Proguard
        minifyEnabled false
        useProguard true
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
}

然后在同一目录下添加一个名为proguard-rules.pro的文件。查看示例应用程序的proguard-rules.pro文件以了解应粘贴的内容。

注意: 如果你不创建proguard-rules.pro文件,那么当你尝试加入会议或者会议屏幕试图打开但立即关闭时,你的应用将会崩溃。你将在日志中看到以下错误之一。

加入会议

_joinMeeting() async {
    try {
	  FeatureFlag featureFlag = FeatureFlag();
	  featureFlag.welcomePageEnabled = false;
	  featureFlag.resolution = FeatureFlagVideoResolution.MD_RESOLUTION; // 限制视频分辨率为360p

      var options = MeetHourMeetingOptions()
        ..room = "TestRoom" // 必填,空格会被删除
        ..serverURL = "https://meethour.io"
        ..subject = "Meeting with John"
        ..userDisplayName = "John Delise"
        ..token = "" // JWT令牌用于用户身份验证作为主持人。
        ..pcode = "" // 动态传递会议密码。
        ..userEmail = "john@gmail.com"
        ..userAvatarURL = "https://someimageurl.com/image.jpg" // 或.png
        ..audioOnly = true
        ..audioMuted = true
        ..videoMuted = true
        ..prejoinPageEnabled = true // 使其为false以跳过PrejoinPage
        ..disableInviteFunctions = true // 禁用移动SDK中的邀请功能
        ..featureFlag = featureFlag;

      await MeetHour.joinMeeting(options);
    } catch (error) {
      debugPrint("error: $error");
    }
  }

MeetHourMeetingOptions

字段 是否必填 默认值 描述
room N/A 唯一的房间名称,将附加到serverURL。有效字符:字母数字、破折号和下划线。
subject $room 会议名称显示在会议顶部。如果为空,默认为房间名称,其中破折号和下划线被替换为空格,并首字母大写。
userDisplayName “Guest” 用户的显示名称。
userEmail none 用户的电子邮件地址。
audioOnly false 无视频开始会议。可以在会议中切换。
audioMuted false 开始会议时静音音频。可以在会议中切换。
videoMuted false 开始会议时静音视频。可以在会议中切换。
serverURL meethour.io 指定自己的托管服务器。必须是一个有效的绝对URL,格式为<scheme>://<host>[/<path>],即https://someHost.com。默认为Meet Hour的服务器。
userAvatarURL N/A none 用户的头像URL。
token N/A none 用于认证的JWT令牌。
pcode N/A none 用于动态传递会议密码的pcode。
prejoinPageEnabled N/A false 使其为false以跳过PrejoinPage。
disableInviteFunctions N/A false 禁用移动SDK中的邀请功能。
featureFlag 见下文 用于启用/禁用功能和设置Meet Hour SDK视频分辨率的对象。

FeatureFlag

特征标志允许你限制视频分辨率并启用/禁用Meet Hour SDK中提到的一些功能。如果你不向MeetHourMeetingOptions提供任何标志,将使用默认值。

标志 Android默认值 iOS默认值 描述
addPeopleEnabled true true 启用蓝色按钮“添加人员”,当你独自一人时显示。需要inviteEnabled标志才能工作。
calendarEnabled true auto 启用日历集成。
callIntegrationEnabled true true 启用呼叫集成(iOS上的CallKit,Android上的ConnectionService)。请注意
closeCaptionsEnabled true true 启用菜单中的字幕选项。
conferenceTimerEnabled true true 启用会议计时器。
chatEnabled true true 启用聊天(按钮和功能)。
inviteEnabled true true 启用菜单中的邀请选项。
iOSRecordingEnabled N/A false 启用iOS中的录制。
kickOutEnabled true true 启用参与者视频缩略图中的踢出选项。
liveStreamingEnabled auto auto 启用菜单中的直播流选项。
meetingNameEnabled true true 显示会议名称。
meetingPasswordEnabled true true 启用菜单中的会议密码选项(如果设置了会议密码,对话框仍然会显示)。
pipEnabled auto auto 启用画中画模式。
raiseHandEnabled true true 启用菜单中的举手选项。
recordingEnabled auto N/A 启用菜单中的录制选项。
resoulution N/A N/A 设置本地和(最大)远程视频分辨率。覆盖服务器配置。接受的值为:LD_RESOLUTION为180p,MD_RESOLUTION为360p,SD_RESOLUTION为480p(SD),HD_RESOLUTION为720p(HD)。
serverURLChangeEnabled true true 启用服务器URL更改。
tileViewEnabled true true 启用菜单中的拼贴视图选项。
toolboxAlwaysVisible true true 工具箱(按钮和菜单)在通话期间始终可见(如果不是,则单击一次显示它)。
videoShareButtonEnabled true true 启用视频共享按钮。
welcomePageEnabled false false 启用欢迎页面。“欢迎页面列出了最近的会议和日历约会,旨在供独立应用程序使用。”

MeetHourMeetingResponse

字段 类型 描述
isSuccess bool 成功指示器。
message String 成功消息或错误作为字符串。
error dynamic 可选,仅当isSuccess为false时存在。错误对象。

监听会议事件

支持的事件

名称 描述
onConferenceWillJoin 会议正在加载。
onConferenceJoined 用户已加入会议。
onConferenceTerminated 用户已退出会议。
onPictureInPictureWillEnter 用户进入画中画模式。
onPictureInPictureTerminated 用户退出画中画模式。
onError 监听会议事件时发生错误。

每个会议事件

要为每个会议监听事件,请在joinMeeting中传递一个MeetHourMeetingListener。当onConferenceTerminated事件触发时,监听器将自动移除。

await MeetHour.joinMeeting(options,
  listener: MeetHourMeetingListener(onConferenceWillJoin: ({message}) {
    debugPrint("${options.room} will join with message: $message");
  }, onConferenceJoined: ({message}) {
    debugPrint("${options.room} joined with message: $message");
  }, onConferenceTerminated: ({message}) {
    debugPrint("${options.room} terminated with message: $message");
  }, onPictureInPictureWillEnter: ({message}) {
	debugPrint("${options.room} entered PIP mode with message: $message");
  }, onPictureInPictureTerminated: ({message}) {
	debugPrint("${options.room} exited PIP mode with message: $message");
  }));

全局会议事件

要监听全局会议事件,只需使用MeetHour.addListener(myListener)添加一个MeetHourListener。你可以使用MeetHour.removeListener(listener)MeetHour.removeAllListeners()来移除监听器。

@override
void initState() {
  super.initState();
  MeetHour.addListener(MeetHourMeetingListener(
    onConferenceWillJoin: _onConferenceWillJoin,
    onConferenceJoined: _onConferenceJoined,
    onConferenceTerminated: _onConferenceTerminated,
    onPictureInPictureWillEnter: _onPictureInPictureWillEnter,
    onPictureInPictureTerminated: _onPictureInPictureTerminated,
    onError: _onError));
}

@override
void dispose() {
  super.dispose();
  MeetHour.removeAllListeners();
}

_onConferenceWillJoin({message}) {
  debugPrint("_onConferenceWillJoin broadcasted");
}

_onConferenceJoined({message}) {
  debugPrint("_onConferenceJoined broadcasted");
}

_onConferenceTerminated({message}) {
  debugPrint("_onConferenceTerminated broadcasted");
}

_onPictureInPictureWillEnter({message}) {
debugPrint("_onPictureInPictureWillEnter broadcasted with message: $message");
}

_onPictureInPictureTerminated({message}) {
debugPrint("_onPictureInPictureTerminated broadcasted with message: $message");
}

_onError(error) {
  debugPrint("_onError broadcasted");
}

程序化关闭会议

MeetHour.closeMeeting();

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

1 回复

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


当然,以下是一个关于如何在Flutter项目中使用meet_hour插件进行会议安排的示例代码。meet_hour插件(假设它存在并且提供了会议安排功能)通常可能包括会议时间选择、参与者管理、会议创建等功能。以下是一个简化的示例,展示如何使用这样的插件。

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

dependencies:
  flutter:
    sdk: flutter
  meet_hour: ^latest_version  # 请替换为实际的最新版本号

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

接下来,在你的Flutter项目中,你可以按照以下步骤使用meet_hour插件:

1. 导入插件

在你的Dart文件中导入meet_hour插件:

import 'package:meet_hour/meet_hour.dart';

2. 初始化插件

你可能需要在应用的某个初始化阶段配置插件,比如设置API密钥或初始化某些参数(这取决于插件的具体要求)。假设meet_hour插件需要初始化:

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await MeetHour.initialize('your_api_key_here');  // 假设插件需要API密钥
  runApp(MyApp());
}

3. 使用插件功能

假设meet_hour插件提供了选择会议时间、添加参与者和创建会议的功能,下面是一个简单的示例页面,展示了如何使用这些功能:

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

class MeetingSchedulerPage extends StatefulWidget {
  @override
  _MeetingSchedulerPageState createState() => _MeetingSchedulerPageState();
}

class _MeetingSchedulerPageState extends State<MeetingSchedulerPage> {
  List<String> participants = [];
  DateTime? selectedTime;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('会议安排'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Text('选择参与者:', style: TextStyle(fontSize: 18)),
            SizedBox(height: 16),
            Expanded(
              child: ListView.builder(
                shrinkWrap: true,
                itemCount: participants.length,
                itemBuilder: (context, index) {
                  return ListTile(
                    title: Text(participants[index]),
                    trailing: IconButton(
                      icon: Icon(Icons.delete),
                      onPressed: () {
                        setState(() {
                          participants.removeAt(index);
                        });
                      },
                    ),
                  );
                },
              ),
            ),
            SizedBox(height: 16),
            ElevatedButton(
              onPressed: () async {
                // 假设插件提供了一个选择参与者的接口
                String newParticipant = await Navigator.push(
                  context,
                  MaterialPageRoute(
                    builder: (context) => ParticipantSelector(),
                  ),
                );
                if (newParticipant != null) {
                  setState(() {
                    participants.add(newParticipant);
                  });
                }
              },
              child: Text('添加参与者'),
            ),
            SizedBox(height: 16),
            Text('选择会议时间:', style: TextStyle(fontSize: 18)),
            SizedBox(height: 16),
            ElevatedButton(
              onPressed: () async {
                // 假设插件提供了一个选择时间的接口
                DateTime? pickedTime = await showDatePicker(
                  context: context,
                  initialDate: selectedTime ?? DateTime.now(),
                  firstDate: DateTime(DateTime.now().year - 1),
                  lastDate: DateTime(DateTime.now().year + 1),
                );
                if (pickedTime != null && pickedTime != selectedTime) {
                  setState(() {
                    selectedTime = pickedTime;
                  });
                }
              },
              child: Text('选择时间'),
            ),
            SizedBox(height: 16),
            Text('已选时间: ${selectedTime?.toLocal()}'),
            SizedBox(height: 16),
            ElevatedButton(
              onPressed: () async {
                if (selectedTime == null || participants.isEmpty) {
                  ScaffoldMessenger.of(context).showSnackBar(
                    SnackBar(content: Text('请选择时间和参与者')),
                  );
                  return;
                }
                
                // 假设插件提供了一个创建会议的接口
                try {
                  await MeetHour.createMeeting(
                    time: selectedTime!,
                    participants: participants,
                  );
                  ScaffoldMessenger.of(context).showSnackBar(
                    SnackBar(content: Text('会议创建成功')),
                  );
                } catch (e) {
                  ScaffoldMessenger.of(context).showSnackBar(
                    SnackBar(content: Text('会议创建失败: $e')),
                  );
                }
              },
              child: Text('创建会议'),
            ),
          ],
        ),
      ),
    );
  }
}

// 假设有一个参与者选择器页面
class ParticipantSelector extends StatelessWidget {
  static Future<String?> show(BuildContext context) {
    final Completer<String?> completer = Completer<String?>();
    showDialog(
      context: context,
      builder: (context) {
        return AlertDialog(
          title: Text('选择参与者'),
          content: TextField(
            onEditingComplete: () {
              Navigator.of(context).pop(completer.future.value);
            },
            onSubmitted: (value) {
              completer.complete(value);
            },
          ),
          actions: <Widget>[
            TextButton(
              onPressed: () {
                Navigator.of(context).pop();
                completer.complete(null);
              },
              child: Text('取消'),
            ),
            TextButton(
              onPressed: () {
                completer.complete(TextField.focusNodeOf(context)?.text);
              },
              child: Text('确定'),
            ),
          ],
        );
      },
    );
    return completer.future;
  }

  @override
  Widget build(BuildContext context) {
    throw UnimplementedError(); // 这个页面不会被实际构建,因为是通过showDialog调用的
  }
}

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

注意

  1. 上述代码假设meet_hour插件提供了initializecreateMeeting等静态方法,以及一个假设的ParticipantSelector页面用于选择参与者。实际情况中,你需要根据插件的实际API文档进行调整。
  2. 时间选择部分使用了Flutter自带的showDatePicker,因为meet_hour插件的具体时间选择功能未知。
  3. 错误处理和边界情况(如空值检查)在实际应用中需要更加严谨。

希望这个示例能帮助你理解如何在Flutter项目中使用meet_hour插件进行会议安排。如果有任何具体的API或功能细节,请参考插件的官方文档。

回到顶部