1.用户加入会议室

2.发言相关
This commit is contained in:
fuenmao 2024-11-28 09:51:54 +08:00
parent 3813d88d0c
commit 3a1dde6e19
16 changed files with 495 additions and 113 deletions

View File

@ -1,4 +1,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<application
android:label="智汇享"
android:name="${applicationName}"

View File

@ -2,6 +2,13 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- 摄像头 -->
<key>NSCameraUsageDescription</key>
<string>App需要您的同意,才能访问摄像头</string>
<!-- 麦克风 -->
<key>NSMicrophoneUsageDescription</key>
<string>App需要您的同意,才能访问麦克风</string>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>

View File

@ -53,4 +53,18 @@ abstract class RetrofitClient {
Future<BaseStructureResult<String>> getMeetingToken(
@Query("roomNum") String roomNum,
);
///
@GET("/room/apply-speak")
Future<BaseStructureResult> applySpeak(
@Query("roomNum") String roomNum,
);
///
@DELETE("/room/manager")
Future<BaseStructureResult> cancelSpeak(
@Field("roomId") String roomId,
@Field("roomNum") String roomNum,
@Field("userId") String userId,
);
}

View File

@ -148,7 +148,7 @@ class TheError extends Interceptor {
break;
case DioExceptionType.badResponse:
if (err.response == null) {
debugPrint('请求进入异常但是请求response');
debugPrint('wgs输出===请求进入异常但是请求response');
} else {
Response? response = err.response;
if (response != null) {

View File

@ -24,7 +24,7 @@ class UserStore extends GetxController with RequestToolMixin {
userInfoEntity.value = UserInfoEntity.fromJson(userDetail);
}
} catch (err) {
debugPrint('$err');
debugPrint('wgs输出===$err');
StorageService.to.remove(AppStorageKey.userInfo.value);
}
if ((token?.isNotEmpty ?? false) && userInfoEntity.value != null) {

View File

@ -85,7 +85,7 @@ class MyApp extends StatelessWidget {
getPages: AppPages.pages,
routingCallback: (routing) {
String? currentRouter = routing?.current;
debugPrint("当前路由:${routing?.current}");
debugPrint("wgs输出===当前路由:${routing?.current}");
},
builder: EasyLoading.init(
builder: (context, child) {

View File

@ -1,17 +1,17 @@
import 'dart:async';
import 'dart:convert';
import 'package:agora_rtc_engine/agora_rtc_engine.dart';
import 'package:flutter/foundation.dart';
import 'package:get/get.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:signalr_core/signalr_core.dart';
import 'package:wgshare/common/store/user_store.dart';
import '../../common/config/request_config.dart';
import '../../common/mixins/request_tool_mixin.dart';
import '../../common/models/common/base_structure_result.dart';
import '../../common/models/meeting_room_info.dart';
import '../../common/models/meeting_room_user.dart';
import '../../utils/permission_handler.dart';
import '../../utils/toast_utils.dart';
import 'meeting_main_state.dart';
@ -34,10 +34,20 @@ class MeetingMainLogic extends GetxController with RequestToolMixin{
super.onClose();
state.memberNameSearchController.dispose();
stopTime();
leaveMeeting();
leaveMeetingToRtc();
leaveMeetingToSocket();
state.hubConnection.value?.stop();
}
/// Token
Future<void> doHttpGetMeetingToken() async {
BaseStructureResult<String> res = await getClient().getMeetingToken(state.roomNumber.value);
state.meetingToken.value = res.data!;
initRtc();
signalRSocket();
}
///
/// 1.
/// 2.
@ -54,26 +64,6 @@ class MeetingMainLogic extends GetxController with RequestToolMixin{
ToastUtils.dismiss();
}
/// Socket长连接
Future<void> signalRSocket() async {
state.hubConnection.value = HubConnectionBuilder().withUrl('${RequestConfig().baseUrl}/session-manage',
HttpConnectionOptions(
transport: HttpTransportType.longPolling,
accessTokenFactory: () async => await Future.value(UserStore.to.token),
logging: (level, message) => debugPrint("SignalR Socket$message"),
)).build();
await state.hubConnection.value?.start();
joinChannelToSocket();
}
///
Future<void> joinChannelToSocket() async {
await state.hubConnection.value?.invoke("joinChannel", args: [state.roomNumber.value, false, false, false]);
mergeFetch();
}
///
void changeMeetingInfoState(bool isShow){
state.isShowMeetingInfoFloatingLayer.value = isShow;
@ -101,15 +91,6 @@ class MeetingMainLogic extends GetxController with RequestToolMixin{
state.cacheUsers.value = meetingRoomUsers;
}
/// Token
Future<void> doHttpGetMeetingToken() async {
BaseStructureResult<String> res = await getClient().getMeetingToken(state.roomNumber.value);
state.meetingToken.value = res.data!;
initRtc();
signalRSocket();
}
///
void startTime(){
state.stopwatch.value.start();
@ -136,8 +117,8 @@ class MeetingMainLogic extends GetxController with RequestToolMixin{
void searchMember(String value){
if(value.isNotEmpty){
List<MeetingRoomUser> memberSearchList = [];
for(var i = 0; i < state.cacheUsers.length; i++){
if(state.cacheUsers[i].userName.contains(value)){
for(var i = 0; i < state.cacheUsers.value.length; i++){
if(state.cacheUsers.value[i].userName.contains(value)){
memberSearchList.add(state.cacheUsers.value[i]);
}
}
@ -147,12 +128,104 @@ class MeetingMainLogic extends GetxController with RequestToolMixin{
}
}
///
Future<void> doHttpApplySpeak() async {
BaseStructureResult res = await getClient().applySpeak(state.roomNumber.value);
}
///
Future<void> doHttpCancelSpeak() async {
BaseStructureResult res = await getClient().cancelSpeak(state.meetingRoomInfo.value!.id, state.meetingRoomInfo.value!.roomNum, UserStore.to.userInfoEntity.value!.uid);
}
/// --------------------------signalR Socket相关
/// Socket长连接
Future<void> signalRSocket() async {
state.hubConnection.value = HubConnectionBuilder().withUrl('${RequestConfig().baseUrl}/session-manage',
HttpConnectionOptions(
transport: HttpTransportType.longPolling,
accessTokenFactory: () async => await Future.value(UserStore.to.token),
logging: (level, message) => debugPrint("wgs输出===SignalR Socket-$message"),
)).build();
await state.hubConnection.value?.start();
joinMeetingToSocket();
///
state.hubConnection.value?.on("UserJoined", (user){
var jsonStr = json.encode(user);
var listDynamic = jsonDecode(jsonStr);
List<MeetingRoomUser> meetingRoomUsers = (listDynamic as List<dynamic>).map((e) => MeetingRoomUser.fromJson((e as Map<String,dynamic>))).toList();
state.cacheUsers.value.addAll(meetingRoomUsers);
state.users.value = state.cacheUsers.value;
debugPrint("wgs输出===Socket-远端用户或主播加入会议室:$jsonStr");
});
///
state.hubConnection.value?.on("UserLeave", (uid){
var jsonStr = json.encode(uid);
List listDynamic = jsonDecode(jsonStr);
for(String uidStr in listDynamic){
for(var j = 0; j < state.cacheUsers.value.length; j++){
if(state.cacheUsers.value[j].uid == uidStr){
state.cacheUsers.value.removeAt(j);
}
}
}
state.users.value = state.cacheUsers.value;
debugPrint("wgs输出===Socket-远端用户或主播离开会议室:$jsonStr");
});
/// /-
state.hubConnection.value?.on("ManagerRefresh", (e){
var jsonStr = json.encode(e?[0]);
var listDynamic = jsonDecode(jsonStr);
MeetingRoomUser meetingRoomUser = MeetingRoomUser.fromJson(listDynamic);
if(meetingRoomUser.isRoomManager == true){
debugPrint("wgs输出===Socket-申请发言:${e?[0]}--${e?[1]}");
for(MeetingRoomUser mru in state.cacheUsers.value){
if(mru.uid == meetingRoomUser.uid){
mru.roleId = meetingRoomUser.roleId;
mru.enableMicr = true;
mru.isRoomManager = meetingRoomUser.isRoomManager;
}
}
state.isSpeak.value = true;
setEnableLocalAudio(true);
}else{
debugPrint("wgs输出===Socket-停止发言:${e?[0]}--${e?[1]}");
for(MeetingRoomUser mru in state.cacheUsers.value){
if(mru.uid == meetingRoomUser.uid){
mru.roleId = meetingRoomUser.roleId;
mru.enableMicr = false;
mru.isRoomManager = meetingRoomUser.isRoomManager;
}
}
state.isSpeak.value = false;
setEnableLocalAudio(false);
}
});
}
///
Future<void> joinMeetingToSocket() async {
await state.hubConnection.value?.invoke("joinChannel", args: [state.roomNumber.value, false, false, false]);
mergeFetch();
}
///
Future<void> leaveMeetingToSocket() async {
await state.hubConnection.value?.invoke("levelChannel", args: [state.roomNumber.value]);
}
/// --------------------------SDK相关
/// SDK
Future<void> initRtc() async {
//
if (defaultTargetPlatform == TargetPlatform.android) {
await [Permission.microphone].request();
}
//
PermissionHandler.requestCameraPermission();
PermissionHandler.requestMicrophonePermission();
// RtcEngine
state.rctEngine.value = createAgoraRtcEngine();
@ -163,29 +236,58 @@ class MeetingMainLogic extends GetxController with RequestToolMixin{
channelProfile: ChannelProfileType.channelProfileLiveBroadcasting,
));
joinChannelToRtc();
//
state.rctEngine.value?.setDefaultAudioRouteToSpeakerphone(false);
joinMeetingToRtc();
//
state.rctEngine.value?.registerEventHandler(
RtcEngineEventHandler(
//
//
onJoinChannelSuccess: (RtcConnection connection, int elapsed) {
debugPrint("自己加入会议室ID${connection.localUid}");
debugPrint("wgs输出===RTC-自己加入会议室ID${connection.localUid}");
},
//
//
onLeaveChannel: (RtcConnection connection, RtcStats stats){
debugPrint("wgs输出===RTC-自己离开会议室ID${connection.localUid}");
},
// -
onUserJoined: (RtcConnection connection, int remoteUid, int elapsed) {
debugPrint("远端用户或主播加入会议室用户或主机的ID$remoteUid");
debugPrint("wgs输出===RTC-远端用户或主播加入会议室用户或主机的ID$remoteUid");
},
//
onUserOffline: (RtcConnection connection, int remoteUid, UserOfflineReasonType reason) {
debugPrint("远端用户或主播离开会议室用户或主机的ID$remoteUid");
// -
onUserOffline: (RtcConnection connection, int remoteUid, UserOfflineReasonType reason) async {
debugPrint("wgs输出===RTC-远端用户或主播离开会议室用户或主机的ID$remoteUid");
},
//
onAudioRoutingChanged: (int routing){
debugPrint("wgs输出===RTC-音频路由切换:$routing");
state.communicationMode.value = routing;
if(routing == 1){
debugPrint("wgs输出===RTC-音频路由切换为听筒");
}else if(routing == 3){
debugPrint("wgs输出===RTC-音频路由切换为扬声器");
}else{
debugPrint("wgs输出===RTC-音频路由切换为外接设备");
}
},
//
onLocalAudioStateChanged: (RtcConnection connection, LocalAudioStreamState state, LocalAudioStreamReason reason){
debugPrint("wgs输出===RTC-音频采集开关:$state");
}
),
);
}
///
Future<void> joinChannelToRtc() async {
Future<void> joinMeetingToRtc() async {
await state.rctEngine.value?.joinChannel(
token: state.meetingToken.value,
channelId: state.roomNumber.value,
@ -199,16 +301,30 @@ class MeetingMainLogic extends GetxController with RequestToolMixin{
publishCameraTrack: false,
//
publishMicrophoneTrack: false,
// clientRoleAudience
clientRoleType: ClientRoleType.clientRoleAudience),
// clientRoleBroadcaster clientRoleAudience
// clientRoleBroadcaster
//
//
clientRoleType: ClientRoleType.clientRoleBroadcaster),
);
}
///
Future<void> leaveMeeting() async {
Future<void> leaveMeetingToRtc() async {
//
await state.rctEngine.value?.leaveChannel();
//
await state.rctEngine.value?.release();
}
///
Future<void> setEnableSpeakerphone(int mode) async {
state.communicationMode.value = mode;
await state.rctEngine.value?.setEnableSpeakerphone(mode == 1 ? false : true);
}
///
Future<void> setEnableLocalAudio(bool enabled) async {
await state.rctEngine.value?.enableLocalAudio(enabled);
}
}

View File

@ -49,6 +49,9 @@ class MeetingMainState {
///
late RxList<MeetingRoomUser> cacheUsers = RxList([]);
///
late RxBool isSpeak = false.obs;
/// signalR
late RxString serviceUrl = "http://192.168.2.9:5192/session-manage".obs;
late Rx<HubConnection?> hubConnection = Rx(null);
@ -64,4 +67,6 @@ class MeetingMainState {
late RxBool isPublishCameraTrack = false.obs;
///
late RxBool isPublishMicrophoneTrack = false.obs;
/// 13
late RxInt communicationMode = 1.obs;
}

View File

@ -60,12 +60,14 @@ class MeetingMainPage extends StatelessWidget {
SizedBox(width: 16.w),
GestureDetector(
child: Image.asset(
'assets/images/meeting_main_audio.png',
state.communicationMode.value == 1 ? 'assets/images/index_copy.png' : state.communicationMode.value == 3 ? 'assets/images/meeting_main_camera_open.png' : 'assets/images/meeting_main_audio.png',
width: 20.w,
height: 20.h,
),
onTap: (){
if(state.communicationMode.value == 1 || state.communicationMode.value == 3){
logic.changeMeetingAudioState(true);
}
},
),
Row(
@ -146,7 +148,7 @@ class MeetingMainPage extends StatelessWidget {
//
Visibility(
visible: true,
child: MeetingMainVoiceComponent(users: state.cacheUsers.value)
child: MeetingMainVoiceComponent(users: state.cacheUsers)
),
//
@ -226,7 +228,7 @@ class MeetingMainPage extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
'assets/images/meeting_main_microphone_default.png',
state.isSpeak.value == true ? 'assets/images/meeting_main_microphone_mute.png' : 'assets/images/meeting_main_microphone_default.png',
width: 22.w,
height: 22.h,
),
@ -235,16 +237,20 @@ class MeetingMainPage extends StatelessWidget {
' 静音 ',
style: TextStyle(
fontSize: 12.sp,
color: ColorUtil.Color_202_202_202),
color: state.isSpeak.value == true ? ColorUtil.Color_85_117_242 : ColorUtil.Color_202_202_202),
)
],
),
onTap: () {
if(state.isSpeak.value == false){
Get.bottomSheet(
isScrollControlled: true,
applySpeakPermissionBottomSheet(
context)
);
}else{
logic.doHttpCancelSpeak();
}
},
),
@ -631,11 +637,12 @@ class MeetingMainPage extends StatelessWidget {
GestureDetector(
child: Column(children: [
Text(
'蓝牙设备',
'听筒',
style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.w500,
color: ColorUtil.Color_85_117_242),
fontWeight: state.communicationMode.value == 1 ? FontWeight.w500 : FontWeight.w400,
color: state.communicationMode.value == 1 ? ColorUtil.Color_85_117_242 : ColorUtil.Color_134_134_134
),
),
Container(
width: double.infinity,
@ -645,31 +652,31 @@ class MeetingMainPage extends StatelessWidget {
)
]),
onTap: (){
logic.setEnableSpeakerphone(1);
logic.changeMeetingAudioState(false);
},
)
);
audioList.add(Column(children: [
Text(
'手机听筒',
style: TextStyle(fontSize: 14.sp, color: ColorUtil.Color_134_134_134),
),
Container(
width: double.infinity,
height: 1.h,
color: ColorUtil.Color_49_47_47,
margin: const EdgeInsets.only(top: 14, bottom: 14),
)
]));
audioList.add(Column(
audioList.add(
GestureDetector(
child: Column(
children: [
Text(
'扬声器',
style:
TextStyle(fontSize: 14.sp, color: ColorUtil.Color_134_134_134),
)
style: TextStyle(
fontSize: 14.sp,
fontWeight: state.communicationMode.value == 3 ? FontWeight.w500 : FontWeight.w400,
color: state.communicationMode.value == 3 ? ColorUtil.Color_85_117_242 : ColorUtil.Color_134_134_134
),
),
],
));
),
onTap: (){
logic.setEnableSpeakerphone(3);
logic.changeMeetingAudioState(false);
},
)
);
return audioList;
}
@ -754,6 +761,7 @@ class MeetingMainPage extends StatelessWidget {
),
),
Expanded(
child: GestureDetector(
child: Container(
height: 44.h,
margin: const EdgeInsets.only(left: 6),
@ -770,6 +778,11 @@ class MeetingMainPage extends StatelessWidget {
),
),
),
onTap: (){
logic.doHttpApplySpeak();
Get.back();
},
),
)
],
),
@ -901,8 +914,8 @@ class MeetingMainPage extends StatelessWidget {
height: 36.h,
alignment: Alignment.center,
child: Text(
state.users[index].userName.length >= 3
? state.users[index].userName.substring(1,state.users[index].userName.length)
state.users[index].userName.length > 3
? state.users[index].userName.substring(state.users[index].userName.length - 2,state.users[index].userName.length)
: state.users[index].userName,
style: TextStyle(
fontSize: 12.sp,
@ -919,6 +932,7 @@ class MeetingMainPage extends StatelessWidget {
),
SizedBox(width: 8.w),
Visibility(
visible: state.users[index].roleId == "1" || state.users[index].roleId == "3" ? true : false,
child: Text(
'主持人',
style: TextStyle(
@ -926,7 +940,16 @@ class MeetingMainPage extends StatelessWidget {
fontWeight: FontWeight.w500,
color: ColorUtil.Color_2_177_136),
),
visible: state.users[index].isRoomManager,
),
Visibility(
visible: state.users[index].roleId == "2" && state.users[index].isRoomManager == true ? true : false,
child: Text(
'发言人',
style: TextStyle(
fontSize: 12.sp,
fontWeight: FontWeight.w500,
color: ColorUtil.Color_2_177_136),
),
)
],
),

View File

@ -29,7 +29,7 @@ class MeetingMainVideoComponent extends StatelessWidget {
pageSnapping: true,
onPageChanged: (index) {
//
debugPrint('index=====$index');
debugPrint('wgs输出===$index');
},
children: <Widget>[
Container(

View File

@ -9,7 +9,7 @@ import 'meeting_main_voice_logic.dart';
import 'meeting_main_voice_state.dart';
class MeetingMainVoiceComponent extends StatelessWidget {
MeetingMainVoiceComponent({Key? key, required this.users}) : super(key: key);
MeetingMainVoiceComponent({super.key, required this.users});
final List<MeetingRoomUser> users;
@ -26,7 +26,7 @@ class MeetingMainVoiceComponent extends StatelessWidget {
behavior: CusBehavior(),
child: GridView.builder(
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: 0.7,
crossAxisSpacing: 20),
@ -44,8 +44,8 @@ class MeetingMainVoiceComponent extends StatelessWidget {
height: 76.h,
alignment: Alignment.center,
child: Text(
users[index].userName.length >= 3
? users[index].userName.substring(1,users[index].userName.length)
users[index].userName.length > 3
? users[index].userName.substring(users[index].userName.length - 2,users[index].userName.length)
: users[index].userName,
style: TextStyle(
fontSize: 22.sp,

View File

@ -25,7 +25,7 @@ Widget $theCachedNetworkImage(ImageWidgetBuilder imageBuilder, {required String
// placeholder: (context, url) => const CircularProgressIndicator(),
placeholder: (context, url) => Center(child: SpinKitWave(color: Theme.of(context).primaryColor, size: 50.r)),
errorListener: (e) {
debugPrint('图片报错.............$e');
debugPrint('wgs输出===图片报错.............$e');
},
errorWidget: (context, url, error) {
return GestureDetector(

View File

@ -17,4 +17,10 @@ class DeviceInfo {
return 'error';
}
}
static Future<int> getAndroidSdkVersion() async {
DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin();
AndroidDeviceInfo deviceInfo = await deviceInfoPlugin.androidInfo;
return deviceInfo.version.sdkInt;
}
}

View File

@ -0,0 +1,97 @@
import 'dart:ffi';
import 'package:permission_handler/permission_handler.dart';
import 'dart:io';
import '../device_info.dart';
class AndroidPermissionHandler {
Future<void> requestAllPermissions() async {
await requestLocationPermission();
await requestBluetoothPermission();
await requestCameraPermission();
await requestStoragePermission();
await requestNotificationPermission();
await requestPhonePermission();
}
Future<void> requestLocationPermission() async {
PermissionStatus status;
if (Platform.isAndroid && await DeviceInfo.getAndroidSdkVersion() >= 29) {
status = await Permission.locationAlways.request();
} else {
status = await Permission.locationWhenInUse.request();
}
if (status.isGranted) {
print("Android: 位置权限已授予");
} else if (status.isPermanentlyDenied) {
print("Android: 位置权限被永久拒绝,请前往设置开启");
} else {
print("Android: 位置权限被拒绝");
}
}
Future<void> requestBluetoothPermission() async {
PermissionStatus status;
if (Platform.isAndroid && await DeviceInfo.getAndroidSdkVersion() >= 31) {
status = (await [
Permission.bluetoothScan,
Permission.bluetoothConnect,
Permission.bluetoothAdvertise,
].request()) as PermissionStatus;
} else {
status = await Permission.bluetooth.request();
}
if (status.isGranted) {
print("Android: 蓝牙权限已授予");
} else {
print("Android: 蓝牙权限被拒绝");
}
}
Future<void> requestCameraPermission() async {
PermissionStatus status = await Permission.camera.request();
if (status.isGranted) {
print("Android: 相机权限已授予");
} else if (status.isPermanentlyDenied) {
print("Android: 相机权限被永久拒绝,请前往设置开启");
} else {
print("Android: 相机权限被拒绝");
}
}
Future<void> requestStoragePermission() async {
PermissionStatus status = await Permission.storage.request();
if (status.isGranted) {
print("Android: 存储权限已授予");
} else if (status.isPermanentlyDenied) {
print("Android: 存储权限被永久拒绝,请前往设置开启");
} else {
print("Android: 存储权限被拒绝");
}
}
Future<void> requestNotificationPermission() async {
if (Platform.isAndroid && await DeviceInfo.getAndroidSdkVersion() >= 33) {
PermissionStatus status = await Permission.notification.request();
if (status.isGranted) {
print("Android: 通知权限已授予");
} else {
print("Android: 通知权限被拒绝");
}
}
}
Future<void> requestPhonePermission() async {
PermissionStatus status = await Permission.phone.request();
if (status.isGranted) {
print("Android: 电话权限已授予");
} else if (status.isPermanentlyDenied) {
print("Android: 电话权限被永久拒绝,请前往设置开启");
} else {
print("Android: 电话权限被拒绝");
}
}
}

View File

@ -0,0 +1,74 @@
import 'package:permission_handler/permission_handler.dart';
class IosPermissionHandler {
Future<void> requestAllPermissions() async {
await requestLocationPermission();
await requestBluetoothPermission();
await requestCameraPermission();
await requestStoragePermission();
await requestNotificationPermission();
await requestPhonePermission();
}
Future<void> requestLocationPermission() async {
PermissionStatus status = await Permission.location.request();
if (status.isGranted) {
print("iOS: 位置权限已授予");
} else if (status.isPermanentlyDenied) {
print("iOS: 位置权限被永久拒绝,请前往设置开启");
} else {
print("iOS: 位置权限被拒绝");
}
}
Future<void> requestBluetoothPermission() async {
PermissionStatus status = await Permission.bluetooth.request();
if (status.isGranted) {
print("iOS: 蓝牙权限已授予");
} else {
print("iOS: 蓝牙权限被拒绝");
}
}
Future<void> requestCameraPermission() async {
PermissionStatus status = await Permission.camera.request();
if (status.isGranted) {
print("iOS: 相机权限已授予");
} else if (status.isPermanentlyDenied) {
print("iOS: 相机权限被永久拒绝,请前往设置开启");
} else {
print("iOS: 相机权限被拒绝");
}
}
Future<void> requestStoragePermission() async {
PermissionStatus status = await Permission.photos.request();
if (status.isGranted) {
print("iOS: 存储权限已授予");
} else if (status.isPermanentlyDenied) {
print("iOS: 存储权限被永久拒绝,请前往设置开启");
} else {
print("iOS: 存储权限被拒绝");
}
}
Future<void> requestNotificationPermission() async {
PermissionStatus status = await Permission.notification.request();
if (status.isGranted) {
print("iOS: 通知权限已授予");
} else {
print("iOS: 通知权限被拒绝");
}
}
Future<void> requestPhonePermission() async {
PermissionStatus status = await Permission.phone.request();
if (status.isGranted) {
print("iOS: 电话权限已授予");
} else if (status.isPermanentlyDenied) {
print("iOS: 电话权限被永久拒绝,请前往设置开启");
} else {
print("iOS: 电话权限被拒绝");
}
}
}

View File

@ -0,0 +1,36 @@
import 'package:flutter/cupertino.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:wgshare/utils/toast_utils.dart';
class PermissionHandler {
///
static Future<void> requestCameraPermission() async {
PermissionStatus status = await Permission.camera.request();
if (status.isGranted) {
debugPrint("wgs输出===:权限-摄像头权限已授予");
} else if (status.isPermanentlyDenied) {
debugPrint("wgs输出===:权限-摄像头权限被永久拒绝,请前往设置开启");
ToastUtils.showError("权限被永久拒绝,请前往设置开启!");
openAppSettings();
} else {
debugPrint("wgs输出===:权限-摄像头权限被拒绝");
ToastUtils.showError("权限被拒绝,可能会导致相关功能不可用!");
}
}
///
static Future<void> requestMicrophonePermission() async {
PermissionStatus status = await Permission.microphone.request();
if (status.isGranted) {
debugPrint("wgs输出===:权限-麦克风权限已授予");
} else if (status.isPermanentlyDenied) {
debugPrint("wgs输出===:权限-麦克风权限被永久拒绝,请前往设置开启");
ToastUtils.showError("权限被永久拒绝,请前往设置开启!");
openAppSettings();
} else {
debugPrint("wgs输出===:权限-麦克风权限被拒绝");
ToastUtils.showError("权限被拒绝,可能会导致相关功能不可用!");
}
}
}