Browse Source

refactor(权限管理): 重构权限处理逻辑并使用统一工具类

- 升级 permission_handler 依赖至 12.0.0+1
- 创建 PermissionUtil 工具类统一处理权限请求逻辑
- 在 AgentController 和 PairingController 中使用 PermissionUtil 替代原有权限检查方法
- 改进权限请求时的用户引导界面和提示信息
- 在权限设置页面集成新的权限请求流程
newdev_shunjiawei
lxm 1 year ago
parent
commit
33bbb4ba7f
  1. 286
      lib/core/utils/permission_util.dart
  2. 21
      lib/modules/agent/controllers/agent_controller.dart
  3. 71
      lib/modules/pairing/controllers/pairing_controller.dart
  4. 225
      lib/modules/settings/views/permissions_view.dart
  5. 2
      pubspec.yaml

286
lib/core/utils/permission_util.dart

@ -1,134 +1,202 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import 'package:app_settings/app_settings.dart'; import 'package:app_settings/app_settings.dart';
import 'dart:developer';
import 'logger.dart';
// class Logger {
// static void e(String tag, String message) {
// debugPrint('[$tag] ERROR: $message');
// }
// }
class PermissionUtil { class PermissionUtil {
/// 检查并请求多个权限 static final PermissionUtil instance = PermissionUtil._();
/// PermissionUtil._();
/// [permissions] 要检查的权限列表,如 [Permission.microphone, Permission.location]
/// [rationaleTitle] 弹窗标题 OverlayEntry? _explanationOverlayEntry;
/// [rationaleMessage] 权限用途说明内容(支持多行)
/// Future<bool> requestPermission({
/// 返回是否所有权限都已被授予 required BuildContext context,
static Future<bool> checkAndRequestPermissions({ required Permission permissionType,
required List<Permission> permissions, required String permissionName,
required String rationaleTitle, required String explanationText,
required List<String> rationaleMessage, required String permanentDenialText,
String settingsButtonText = '前往设置',
String cancelButtonText = '取消',
}) async { }) async {
try { try {
Map<Permission, PermissionStatus> statuses = {}; final PermissionStatus status = await permissionType.status;
log('初始权限状态 for $permissionName: $status');
// 检查是否都已授权
bool allGranted = true; // 处理已授权状态(包括授予状态和临时通知权限)
for (var permission in permissions) { if (status == PermissionStatus.granted ||
final status = await permission.status; status == PermissionStatus.provisional) {
if (!status.isGranted) { log('$permissionName 权限已授予或临时授权。');
allGranted = false; return true;
statuses[permission] = status; }
// 处理永久拒绝状态
if (status == PermissionStatus.permanentlyDenied) {
log('$permissionName 权限被永久拒绝,显示引导对话框。');
await _showPermanentDenialDialog(
context: context,
permissionName: permissionName,
permanentDenialText: permanentDenialText,
settingsButtonText: settingsButtonText,
cancelButtonText: cancelButtonText,
);
return false;
}
// 处理初始拒绝/未决定状态
if (status == PermissionStatus.denied) {
log('$permissionName 权限被拒绝或未决定,显示说明横幅并请求。');
_showExplanationOverlay(
context: context,
permissionName: permissionName,
explanationText: explanationText,
);
final PermissionStatus newStatus = await permissionType.request();
log('请求后 $permissionName 的新权限状态: $newStatus');
_hideExplanationOverlay();
// 处理请求后的状态
if (newStatus == PermissionStatus.granted ||
newStatus == PermissionStatus.provisional) {
log('$permissionName 权限在请求后被授予或临时授权。');
return true;
} else if (newStatus == PermissionStatus.permanentlyDenied) {
log('$permissionName 权限在请求后被永久拒绝,再次显示引导对话框。');
await _showPermanentDenialDialog(
context: context,
permissionName: permissionName,
permanentDenialText: permanentDenialText,
settingsButtonText: settingsButtonText,
cancelButtonText: cancelButtonText,
);
} }
return false;
} }
if (allGranted) return true; // 处理受限状态
if (status == PermissionStatus.restricted ||
// 判断是否包含永久拒绝 status == PermissionStatus.limited) {
bool hasPermanentDenied = log('$permissionName 权限受限或受限访问: $status');
statuses.values.any((status) => status.isPermanentlyDenied); await _showRestrictedPermissionDialog(context, permissionName);
return false;
// 显示提示弹窗 }
_showPermissionDialog(
isPermanent: hasPermanentDenied,
title: rationaleTitle,
messages: rationaleMessage,
onConfirm: () async {
if (hasPermanentDenied) {
AppSettings.openAppSettings();
} else {
final newStatuses = await permissions.request();
final allGrantedNow =
newStatuses.values.every((status) => status.isGranted);
if (!allGrantedNow) {
print("部分权限被拒绝: $newStatuses");
}
}
},
);
// 其他未知状态处理
log('$permissionName 处于未知状态: $status');
return false; return false;
} catch (e) { } catch (e) {
print("权限检查异常: $e"); Logger.e('PermissionUtil', '请求 $permissionName 权限失败: $e');
_hideExplanationOverlay();
return false; return false;
} }
} }
/// 权限说明弹窗 void _showExplanationOverlay({
static void _showPermissionDialog({ required BuildContext context,
required bool isPermanent, required String permissionName,
required String title, required String explanationText,
required List<String> messages,
required VoidCallback onConfirm,
}) { }) {
Get.generalDialog( _hideExplanationOverlay(); // 确保移除旧叠加层
barrierDismissible: false,
barrierLabel: '权限提示', _explanationOverlayEntry = OverlayEntry(
transitionBuilder: (context, anim1, anim2, child) { builder: (context) => Positioned(
return SlideTransition( top: MediaQuery.of(context).padding.top + 10,
position: Tween<Offset>( left: 16,
begin: Offset(0, -1), right: 16,
end: Offset(0, 0), child: Material(
).animate(CurvedAnimation(parent: anim1, curve: Curves.easeOut)), borderRadius: BorderRadius.circular(12),
child: child, elevation: 4,
); color: Colors.white,
}, child: Padding(
pageBuilder: (context, anim1, anim2) { padding: const EdgeInsets.all(16),
return SafeArea( child: Column(
child: Align( mainAxisSize: MainAxisSize.min,
alignment: Alignment.topCenter, crossAxisAlignment: CrossAxisAlignment.start,
child: Padding( children: [
padding: const EdgeInsets.only(top: 80, left: 24, right: 24), Text(
child: Material( '需要$permissionName权限',
borderRadius: BorderRadius.circular(12), style: const TextStyle(
color: Colors.white, fontWeight: FontWeight.bold,
child: Padding( fontSize: 16,
padding: const EdgeInsets.all(16),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title,
style: TextStyle(
fontSize: 18, fontWeight: FontWeight.bold)),
const SizedBox(height: 12),
...messages.map((msg) => Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Text("• $msg"),
)),
const SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () => Get.back(),
child: const Text('取消'),
),
const SizedBox(width: 8),
TextButton(
onPressed: () {
Get.back();
onConfirm();
},
child: Text(isPermanent ? '前往设置' : '授权'),
),
],
),
],
), ),
), ),
), const SizedBox(height: 8),
Text(
explanationText,
style: const TextStyle(fontSize: 14),
),
],
), ),
), ),
); ),
}, ),
);
Future.delayed(Duration.zero, () {
if (_explanationOverlayEntry != null) {
Overlay.of(context).insert(_explanationOverlayEntry!);
log('权限说明叠加层已显示。');
}
});
}
void _hideExplanationOverlay() {
_explanationOverlayEntry?.remove();
_explanationOverlayEntry = null;
}
Future<void> _showPermanentDenialDialog({
required BuildContext context,
required String permissionName,
required String permanentDenialText,
required String settingsButtonText,
required String cancelButtonText,
}) async {
await showDialog<void>(
context: context,
barrierDismissible: false,
builder: (context) => AlertDialog(
title: Text('需要$permissionName权限'),
content: Text(permanentDenialText),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: Text(cancelButtonText),
),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
AppSettings.openAppSettings();
},
child: Text(settingsButtonText),
),
],
),
);
}
Future<void> _showRestrictedPermissionDialog(
BuildContext context, String permissionName) async {
await showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('$permissionName权限受限'),
content: const Text('此功能在当前设备上不可用或受限,请检查设备设置或系统限制'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('确定'),
),
],
),
); );
} }
} }

21
lib/modules/agent/controllers/agent_controller.dart

@ -698,7 +698,16 @@ class AgentController extends GetxController {
// 如果要切换到语音模式,需要先检查麦克风权限 // 如果要切换到语音模式,需要先检查麦克风权限
if (isTextInputMode.value) { if (isTextInputMode.value) {
final hasPermission = await _checkMicrophonePermission(); // 使用 PermissionUtil 检查麦克风权限
final hasPermission = await PermissionUtil.instance.requestPermission(
context: Get.context!,
permissionType: Permission.microphone,
permissionName: '麦克风',
explanationText: '语音识别与翻译功能需要访问麦克风。',
permanentDenialText: '您已永久拒绝麦克风权限,请前往系统设置开启权限。',
settingsButtonText: '去设置',
cancelButtonText: '取消',
);
if (!hasPermission) { if (!hasPermission) {
return; // 权限未获取,保持文本模式 return; // 权限未获取,保持文本模式
} }
@ -871,7 +880,15 @@ class AgentController extends GetxController {
if (isListening.value) return; if (isListening.value) return;
// 检查麦克风权限 // 检查麦克风权限
final hasPermission = await _checkMicrophonePermission(); final hasPermission = await PermissionUtil.instance.requestPermission(
context: Get.context!,
permissionType: Permission.microphone,
permissionName: '麦克风',
explanationText: '语音识别与翻译功能需要访问麦克风。',
permanentDenialText: '您已永久拒绝麦克风权限,请前往系统设置开启权限。',
settingsButtonText: '去设置',
cancelButtonText: '取消',
);
if (!hasPermission) { if (!hasPermission) {
isTextInputMode.value = true; // 如果没有权限,切回文本模式 isTextInputMode.value = true; // 如果没有权限,切回文本模式
return; return;

71
lib/modules/pairing/controllers/pairing_controller.dart

@ -7,6 +7,7 @@ import '../../../core/utils/logger.dart';
import 'dart:io'; import 'dart:io';
import '../../../data/services/bluetooth_manager.dart'; import '../../../data/services/bluetooth_manager.dart';
import '../../../data/services/ble_manager.dart'; import '../../../data/services/ble_manager.dart';
import '../../../core/utils/permission_util.dart';
/// 配对状态枚举 /// 配对状态枚举
enum PairingState { enum PairingState {
@ -359,7 +360,7 @@ class PairingController extends GetxController {
if (success) { if (success) {
Logger.i(_tag, 'BLE配对启动成功,立即注册后台扫描'); Logger.i(_tag, 'BLE配对启动成功,立即注册后台扫描');
// 配对启动成功后立即注册后台扫描 // 配对启动成功后立即注册后台扫描
try { try {
// Android平台注册后台扫描 // Android平台注册后台扫描
@ -372,7 +373,7 @@ class PairingController extends GetxController {
Logger.e(_tag, '注册后台扫描失败: $e'); Logger.e(_tag, '注册后台扫描失败: $e');
// 注册失败不影响配对流程继续 // 注册失败不影响配对流程继续
} }
Logger.i(_tag, '等待BLE连接完成...'); Logger.i(_tag, '等待BLE连接完成...');
// 配对结果将通过BLE状态监听回调处理 // 配对结果将通过BLE状态监听回调处理
} else { } else {
@ -508,23 +509,75 @@ class PairingController extends GetxController {
return true; return true;
} }
// Android权限检查 // Android权限检查 - 使用 PermissionUtil
final permissions = <Permission>[]; final context = Get.context;
permissions.add(Permission.locationWhenInUse); if (context == null) {
Logger.e(_tag, '无法获取有效上下文,跳过权限请求');
return false;
}
// 根据 Android 版本确定需要的权限
List<Permission> requiredPermissions = [
Permission.locationWhenInUse,
];
if (Platform.isAndroid && await _getAndroidSdkVersion() >= 31) { if (Platform.isAndroid && await _getAndroidSdkVersion() >= 31) {
permissions.add(Permission.bluetoothScan); requiredPermissions.add(Permission.bluetoothScan);
permissions.add(Permission.bluetoothConnect); requiredPermissions.add(Permission.bluetoothConnect);
} }
final statuses = await permissions.request(); // 逐个请求权限
return statuses.values.every((status) => status.isGranted); for (var permission in requiredPermissions) {
final permissionName = _getPermissionName(permission);
Logger.d(_tag, '正在请求 $permissionName 权限');
final granted = await PermissionUtil.instance.requestPermission(
context: context,
permissionType: permission,
permissionName: permissionName,
explanationText: _getExplanationText(permission),
permanentDenialText: '您已永久拒绝$permissionName权限,请前往应用设置开启',
settingsButtonText: '去设置',
cancelButtonText: '取消',
);
if (!granted) {
Logger.w(_tag, '$permissionName 权限未授予');
return false;
}
}
return true;
} catch (e) { } catch (e) {
Logger.e(_tag, '权限检查失败: $e'); Logger.e(_tag, '权限检查失败: $e');
return false; return false;
} }
} }
// 获取权限的用户友好名称
String _getPermissionName(Permission permission) {
if (permission == Permission.location) return '定位';
if (permission == Permission.locationWhenInUse) return '定位';
if (permission == Permission.bluetoothScan) return '蓝牙扫描';
if (permission == Permission.bluetoothConnect) return '蓝牙连接';
return '未知权限';
}
// 获取权限说明文本
String _getExplanationText(Permission permission) {
if (permission == Permission.location ||
permission == Permission.locationWhenInUse) {
return '用于发现和连接蓝牙设备。我们不会在应用后台使用定位功能。';
}
if (permission == Permission.bluetoothScan ||
permission == Permission.bluetoothConnect) {
return '用于扫描和连接蓝牙设备,这是与耳机通信所必需的权限。';
}
return '此权限是应用正常运行所必需的。';
}
/// 设置蓝牙状态监听 /// 设置蓝牙状态监听
void _setupBluetoothListener() { void _setupBluetoothListener() {
_bluetoothStateSubscription?.cancel(); _bluetoothStateSubscription?.cancel();

225
lib/modules/settings/views/permissions_view.dart

@ -4,6 +4,7 @@ import 'package:permission_handler/permission_handler.dart';
import '../controllers/settings_controller.dart'; import '../controllers/settings_controller.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'dart:ui'; import 'dart:ui';
import 'package:deep_voice/core/utils/permission_util.dart'; // 引入PermissionUtil
class PermissionsView extends GetView<SettingsController> { class PermissionsView extends GetView<SettingsController> {
const PermissionsView({Key? key}) : super(key: key); const PermissionsView({Key? key}) : super(key: key);
@ -11,7 +12,7 @@ class PermissionsView extends GetView<SettingsController> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isDarkMode = Theme.of(context).brightness == Brightness.dark; final isDarkMode = Theme.of(context).brightness == Brightness.dark;
return Scaffold( return Scaffold(
backgroundColor: isDarkMode ? Colors.black : Colors.grey[50], backgroundColor: isDarkMode ? Colors.black : Colors.grey[50],
appBar: AppBar( appBar: AppBar(
@ -41,45 +42,53 @@ class PermissionsView extends GetView<SettingsController> {
if (snapshot.connectionState == ConnectionState.waiting) { if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator()); return const Center(child: CircularProgressIndicator());
} }
if (snapshot.hasError) { if (snapshot.hasError) {
return Center(child: Text('获取权限状态失败: ${snapshot.error}'.tr)); return Center(child: Text('获取权限状态失败: ${snapshot.error}'.tr));
} }
final permissionStatuses = snapshot.data ?? {}; final permissionStatuses = snapshot.data ?? {};
return ListView( return ListView(
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h), padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 16.h),
children: [ children: [
// 通讯权限组 // 通讯权限组
_buildPermissionSection('通讯权限', Icons.phone_android, [ _buildPermissionSection(
Permission.phone, '通讯权限',
Permission.sms Icons.phone_android,
], permissionStatuses, isDarkMode), [Permission.phone, Permission.sms],
permissionStatuses,
isDarkMode),
SizedBox(height: 20.h), SizedBox(height: 20.h),
// 日历权限组 // 日历权限组
_buildPermissionSection('日历权限', Icons.calendar_today, [ _buildPermissionSection(
Permission.calendarFullAccess '日历权限',
], permissionStatuses, isDarkMode), Icons.calendar_today,
[Permission.calendarFullAccess],
permissionStatuses,
isDarkMode),
SizedBox(height: 20.h), SizedBox(height: 20.h),
// 位置权限组 // 位置权限组
_buildPermissionSection('位置权限', Icons.location_on, [ _buildPermissionSection('位置权限', Icons.location_on,
Permission.location [Permission.location], permissionStatuses, isDarkMode),
], permissionStatuses, isDarkMode),
], ],
); );
}, },
), ),
); );
} }
// 构建权限组区域 // 构建权限组区域
Widget _buildPermissionSection(String title, IconData icon, List<Permission> permissions, Widget _buildPermissionSection(
Map<Permission, PermissionStatus> statuses, bool isDarkMode) { String title,
IconData icon,
List<Permission> permissions,
Map<Permission, PermissionStatus> statuses,
bool isDarkMode) {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -96,7 +105,7 @@ class PermissionsView extends GetView<SettingsController> {
), ),
), ),
), ),
// 权限项容器 // 权限项容器
Container( Container(
decoration: BoxDecoration( decoration: BoxDecoration(
@ -119,7 +128,8 @@ class PermissionsView extends GetView<SettingsController> {
? ImageFilter.blur(sigmaX: 5, sigmaY: 5) ? ImageFilter.blur(sigmaX: 5, sigmaY: 5)
: ImageFilter.blur(sigmaX: 0, sigmaY: 0), : ImageFilter.blur(sigmaX: 0, sigmaY: 0),
child: Column( child: Column(
children: _buildPermissionItems(permissions, statuses, isDarkMode), children:
_buildPermissionItems(permissions, statuses, isDarkMode),
), ),
), ),
), ),
@ -127,52 +137,52 @@ class PermissionsView extends GetView<SettingsController> {
], ],
); );
} }
// 构建权限项列表 // 构建权限项列表
List<Widget> _buildPermissionItems(List<Permission> permissions, List<Widget> _buildPermissionItems(List<Permission> permissions,
Map<Permission, PermissionStatus> statuses, bool isDarkMode) { Map<Permission, PermissionStatus> statuses, bool isDarkMode) {
final List<Widget> items = []; final List<Widget> items = [];
// 获取描述文本 // 获取名称
String getDescription(Permission permission) { String getName(Permission permission) {
switch (permission) { switch (permission) {
case Permission.phone: case Permission.phone:
return '用于拨打电话功能'; return '电话';
case Permission.sms: case Permission.sms:
return '用于发送短信功能'; return '短信';
case Permission.calendarWriteOnly: case Permission.calendarWriteOnly:
return '用于创建日历事件和提醒'; return '日历写入';
case Permission.calendarFullAccess: case Permission.calendarFullAccess:
return '用于读取和管理日历事件及提醒'; return '日历完整访问';
case Permission.location: case Permission.location:
return '用于获取当前位置信息'; return '位置信息';
default: default:
return '应用功能需要'; return permission.toString();
} }
} }
// 获取名称 // 获取描述文本
String getName(Permission permission) { String getDescription(Permission permission) {
switch (permission) { switch (permission) {
case Permission.phone: case Permission.phone:
return '电话'; return '用于拨打电话功能';
case Permission.sms: case Permission.sms:
return '短信'; return '用于发送短信功能';
case Permission.calendarWriteOnly: case Permission.calendarWriteOnly:
return '日历写入'; return '用于创建日历事件和提醒';
case Permission.calendarFullAccess: case Permission.calendarFullAccess:
return '日历完整访问'; return '用于读取和管理日历事件及提醒';
case Permission.location: case Permission.location:
return '位置信息'; return '用于获取当前位置信息';
default: default:
return permission.toString(); return '应用功能需要';
} }
} }
for (int i = 0; i < permissions.length; i++) { for (int i = 0; i < permissions.length; i++) {
final permission = permissions[i]; final permission = permissions[i];
final status = statuses[permission] ?? PermissionStatus.denied; final status = statuses[permission] ?? PermissionStatus.denied;
items.add( items.add(
_buildPermissionItem( _buildPermissionItem(
name: getName(permission), name: getName(permission),
@ -182,7 +192,7 @@ class PermissionsView extends GetView<SettingsController> {
isDarkMode: isDarkMode, isDarkMode: isDarkMode,
), ),
); );
// 添加分隔线,最后一个不加 // 添加分隔线,最后一个不加
if (i < permissions.length - 1) { if (i < permissions.length - 1) {
items.add(Divider( items.add(Divider(
@ -191,10 +201,10 @@ class PermissionsView extends GetView<SettingsController> {
)); ));
} }
} }
return items; return items;
} }
// 构建单个权限项 // 构建单个权限项
Widget _buildPermissionItem({ Widget _buildPermissionItem({
required String name, required String name,
@ -248,12 +258,12 @@ class PermissionsView extends GetView<SettingsController> {
), ),
); );
} }
// 状态文本 // 状态文本
Widget _buildStatusText(PermissionStatus status, bool isDarkMode) { Widget _buildStatusText(PermissionStatus status, bool isDarkMode) {
String text; String text;
Color color; Color color;
if (status.isGranted) { if (status.isGranted) {
text = '已授权'.tr; text = '已授权'.tr;
color = Colors.green; color = Colors.green;
@ -262,7 +272,7 @@ class PermissionsView extends GetView<SettingsController> {
color = Colors.red; color = Colors.red;
} else if (status.isPermanentlyDenied) { } else if (status.isPermanentlyDenied) {
text = '需要在系统设置中开启'.tr; text = '需要在系统设置中开启'.tr;
color = Colors.grey; color = Colors.orange;
} else if (status.isLimited) { } else if (status.isLimited) {
text = '部分授权'.tr; text = '部分授权'.tr;
color = Colors.orange; color = Colors.orange;
@ -270,7 +280,7 @@ class PermissionsView extends GetView<SettingsController> {
text = '未知状态'.tr; text = '未知状态'.tr;
color = Colors.grey; color = Colors.grey;
} }
return Text( return Text(
text, text,
style: TextStyle( style: TextStyle(
@ -280,45 +290,84 @@ class PermissionsView extends GetView<SettingsController> {
), ),
); );
} }
// 处理权限请求 // 处理权限请求
Future<void> _handlePermissionAction(Permission permission, PermissionStatus status) async { Future<void> _handlePermissionAction(
if (status.isPermanentlyDenied) { Permission permission, PermissionStatus status) async {
// 打开应用设置 try {
controller.openSettings(); final context = Get.context;
return; if (context == null) return;
}
final permissionName = _getPermissionName(permission);
if (!status.isGranted) { final explanationText = _getExplanationText(permission);
// 请求权限
final result = await controller.requestPermission(permission); // 使用 PermissionUtil 请求权限
final hasPermission = await PermissionUtil.instance.requestPermission(
if (result.isGranted) { context: context,
Get.snackbar('成功'.tr, '权限已授予'.tr, snackPosition: SnackPosition.BOTTOM); permissionType: permission,
} else if (result.isPermanentlyDenied) { permissionName: permissionName,
Get.dialog( explanationText: explanationText,
AlertDialog( permanentDenialText: '您已永久拒绝$permissionName权限,请前往应用设置开启',
title: Text('需要权限'.tr), settingsButtonText: '去设置',
content: Text('此权限需要在设置中手动开启'.tr), cancelButtonText: '取消',
actions: [ );
TextButton(
onPressed: () => Get.back(), if (hasPermission) {
child: Text('取消'.tr), Get.snackbar(
), '成功'.tr,
TextButton( '$permissionName权限已授予'.tr,
onPressed: () { snackPosition: SnackPosition.BOTTOM,
Get.back(); backgroundColor: Colors.green,
controller.openSettings(); colorText: Colors.white,
},
child: Text('去设置'.tr),
),
],
),
); );
} }
// 刷新界面 // 刷新界面,重新获取权限状态
Get.forceAppUpdate(); Get.forceAppUpdate();
} catch (e) {
Get.snackbar(
'错误'.tr,
'请求权限时出错: $e'.tr,
snackPosition: SnackPosition.BOTTOM,
backgroundColor: Colors.red,
colorText: Colors.white,
);
}
}
// 获取权限的用户友好名称
String _getPermissionName(Permission permission) {
switch (permission) {
case Permission.phone:
return '电话';
case Permission.sms:
return '短信';
case Permission.calendarWriteOnly:
return '日历写入';
case Permission.calendarFullAccess:
return '日历完整访问';
case Permission.location:
return '位置信息';
default:
return permission.toString();
}
}
// 获取权限说明文本
String _getExplanationText(Permission permission) {
switch (permission) {
case Permission.phone:
return '拨打电话功能需要电话权限';
case Permission.sms:
return '发送短信功能需要短信权限';
case Permission.calendarWriteOnly:
return '创建日历事件需要日历写入权限';
case Permission.calendarFullAccess:
return '管理日历事件需要完整日历权限';
case Permission.location:
return '获取位置信息需要位置权限';
default:
return '应用需要此权限以提供完整功能';
} }
} }
} }

2
pubspec.yaml

@ -21,7 +21,7 @@ dependencies:
audio_session: ^0.1.18 audio_session: ^0.1.18
flutter_markdown: ^0.7.7+1 flutter_markdown: ^0.7.7+1
markdown: ^7.1.1 markdown: ^7.1.1
permission_handler: ^11.4.0 permission_handler: ^12.0.0+1
web_socket_channel: ^3.0.2 web_socket_channel: ^3.0.2
flutter_dotenv: ^5.1.0 flutter_dotenv: ^5.1.0
synchronized: ^3.1.0 synchronized: ^3.1.0

Loading…
Cancel
Save