Flutter会议安排插件meet_hour的使用
Flutter会议安排插件meet_hour的使用
Meet Hour SDK for Flutter。支持Web、Android和iOS平台。
“Meet Hour是一个高清质量视频会议解决方案,并且具有端到端加密等多项功能,例如大厅模式、直播流媒体连接用于筹款活动、视频通话录制、YouTube直播流等。” 请注册开发者或以上计划以使用此SDK。访问 https://meethour.io 获取更多详情。
支持Web、Android & iOS
示例项目
- Web: https://github.com/v-empower/MeetHour-Web-MobileSDKs/tree/master/Web/Flutter/MeetHourSDKTest
- Android & iOS: https://github.com/v-empower/MeetHour-Web-MobileSDKs/tree/master/Mobile/Flutter/MeetHourSDKTest
Pub.dev
meet_hour: '>=5.0.24'
MeetHour SDK 实施 - 注册开发者或以上计划
- SDK 示例链接: https://github.com/v-empower/MeetHour-Web-MobileSDKs
- API 文档链接: https://docs.v-empower.com/docs/MeetHour-API/
目录
支持的API端点
-
获取访问令牌端点:
LoginType LoginObject = LoginType( client_id: '', client_secret: '', grant_type: 'password', password: '', username: '' ); Map<String, dynamic> response = await ApiServices.login(LoginObject);
需要传递相应的值来获取所需响应。
-
安排会议:
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);
-
生成JWT令牌端点:
GenerateJwtType JWTObject = GenerateJwtType( config: '', contactid: '', meetingid: '', uiconfig: '' ); Map<String, dynamic> response = await ApiServices.generateJwt(token, JWTObject);
-
获取用户详细信息:
Map<String, dynamic> response = await ApiServices.userDetails(token);
-
使用刷新令牌获取访问令牌:
RefreshTokenType RefreshTokenObject = RefreshTokenType( client_id: '', client_secret: '', grant_type: '', refresh_token: '' ) Map<String, dynamic> response = await ApiServices.getRefreshToken(token, RefreshTokenObject);
-
将联系人添加到Meet Hour数据库:
AddContactType AddContactObject = AddContactType( countrycode: '', email: '', firstname: '', image: '', is_show_portal: '', lastname: '', phone: '' ); Map<String, dynamic> response = await ApiServices.addContact(token, AddContactObject);
-
获取各国时区列表:
Map<String, dynamic> response = await ApiServices.timezone(token);
-
获取Meet Hour帐户中所有联系人列表:
ContactsType AddContactObject = ContactsType( exclude_hosts: 1100, limit: 10, page: 1 ); Map<String, dynamic> response = await ApiServices.contactsList(token, AddContactObject);
-
更改现有联系人的详细信息:
EditContactType EditContactObject = EditContactType( contactid: 1150; countrycode: '', email: '', firstname: '', image: '', is_show_portal: '', lastname: '', phone: '' ); Map<String, dynamic> response = await ApiServices.editContact(token, EditContactObject);
-
获取即将召开的会议:
UpcomingMeetingType UpcomingMeetingObject = UpcomingMeetingType( page: 10, limit: 10, show_all: 1 ); Map<String, dynamic> response = await ApiServices.upcomingMeetings(token, UpcomingMeetingObject);
-
存档会议:
ArchiveMeetingType ArchiveMeetingObject = ArchiveMeetingType( id: 10 ); Map<String, dynamic> response = await ApiServices.archiveMeeting(token, ArchiveMeetingObject);
-
获取错过会议的详细信息:
MissedMeetingType MissedMeetingObject = MissedMeetingType( limit: 10, page: number, show_all: 1 ); Map<String, dynamic> response = await ApiServices.missedMeetings(token, MissedMeetingObject);
-
获取已完成的会议:
CompletedMeetingType CompletedMeetingObject = CompletedMeetingType( limit: 10, page: number, show_all: 1 ); Map<String, dynamic> response = await ApiServices.completedMeetings(token, CompletedMeetingObject);
-
编辑现有会议:
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);
-
查看会议:
ViewMeetingType ViewMeetingObject = ViewMeetingType( meeting_id: '' ); Map<String, dynamic> response = await ApiServices.viewMeeting(token, ViewMeetingObject);
-
获取所有录制列表:
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
更多关于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(),
);
}
}
注意
- 上述代码假设
meet_hour
插件提供了initialize
、createMeeting
等静态方法,以及一个假设的ParticipantSelector
页面用于选择参与者。实际情况中,你需要根据插件的实际API文档进行调整。 - 时间选择部分使用了Flutter自带的
showDatePicker
,因为meet_hour
插件的具体时间选择功能未知。 - 错误处理和边界情况(如空值检查)在实际应用中需要更加严谨。
希望这个示例能帮助你理解如何在Flutter项目中使用meet_hour
插件进行会议安排。如果有任何具体的API或功能细节,请参考插件的官方文档。