Commit 1daca20d5640a9c1ca1059dd67d2d5f74e44808a

Authored by 吴启风
1 parent 1f5969b8

pref:权限申请页面优化

lib/common/permission/permissionRequestPage.dart
  1 +import 'dart:async';
1 2 import 'dart:io';
2 3 import 'package:flutter/material.dart';
  4 +import 'package:package_info_plus/package_info_plus.dart';
3 5 import 'package:permission_handler/permission_handler.dart';
4 6 import 'package:wow_english/common/core/app_config_helper.dart';
5 7  
6 8 import '../../utils/log_util.dart';
7 9  
8   -/// 权限检查及请求
  10 +/// 带隐私合规功能的权限检查及请求
9 11 /// 外部可通过此方法来进行权限的检查和请求,将自动跳转到`PermissionRequestPage`页面。
10 12 /// 传入 `Permission` 以及对应的权限名称 `permissionTypeStr`,如果有权限则返回 `Future true`
11 13 /// `isRequiredPermission` 如果为 `true`,则 "取消" 按钮将执行 "退出app" 的操作
  14 +Future<bool> permissionCheckAndRequests(
  15 + BuildContext context,
  16 + List<Permission> permissions,
  17 + List<String> permissionTypeStrs,
  18 + {bool isRequiredPermission = false}) async {
  19 +
  20 + // List<PermissionStatus> statuses = await Future.wait(
  21 + // permissions.map((permission) => permission.status),
  22 + // );
  23 + // bool allGranted = statuses.every((status) => status.isGranted);
  24 +
  25 + bool allGranted = await isPermissionsGranted(permissions);
  26 + if (allGranted) {
  27 + return true;
  28 + } else {
  29 + return await Navigator.of(context).push(PageRouteBuilder(
  30 + opaque: false,
  31 + pageBuilder: ((context, animation, secondaryAnimation) {
  32 + return PermissionRequestPage(permissions, permissionTypeStrs,
  33 + isRequiredPermission: isRequiredPermission);
  34 + })));
  35 + }
  36 +}
  37 +
12 38 Future<bool> permissionCheckAndRequest(
13 39 BuildContext context,
14 40 Permission permission,
15 41 String permissionTypeStr,
16 42 {bool isRequiredPermission = false}) async {
17   - if (!await permission.status.isGranted) {
18   - await Navigator.of(context).push(PageRouteBuilder(
19   - opaque: false,
20   - pageBuilder: ((context, animation, secondaryAnimation) {
21   - return PermissionRequestPage(permission, permissionTypeStr,
22   - isRequiredPermission: isRequiredPermission);
23   - })));
24   - } else {
25   - return true;
  43 + return permissionCheckAndRequests(context, [permission], [permissionTypeStr],
  44 + isRequiredPermission: isRequiredPermission);
  45 + }
  46 +
  47 +///判断权限数组是否都授予
  48 +Future<bool> isPermissionsGranted(List<Permission> permissions) async {
  49 + // 使用 every 直接检查权限状态
  50 + return await Future.wait(permissions.map((permission) async {
  51 + return await permission.status.isGranted;
  52 + })).then((statuses) => statuses.every((status) => status));
  53 +}
  54 +
  55 +///请求权限
  56 +Future<MapEntry<Permission, PermissionStatus>?> requestPermissions(List<Permission> permissionList) async {
  57 + Map<Permission, PermissionStatus> statusesMap = await permissionList.request();
  58 + for (var entry in statusesMap.entries) {
  59 + if (!entry.value.isGranted) {
  60 + return entry;
  61 + }
26 62 }
27   - return false;
  63 + return null;
28 64 }
29 65  
30 66 class PermissionRequestPage extends StatefulWidget {
31   - const PermissionRequestPage(this.permission, this.permissionTypeStr,
  67 + const PermissionRequestPage(this.permissions, this.permissionTypeStrs,
32 68 {super.key, this.isRequiredPermission = false});
33 69  
34   - final Permission permission;
35   - final String permissionTypeStr;
  70 + final List<Permission> permissions;
  71 + final List<String> permissionTypeStrs;
  72 + ///是否需要强制授予
36 73 final bool isRequiredPermission;
37 74  
38 75 @override
... ... @@ -49,13 +86,14 @@ class _PermissionRequestPageState extends State&lt;PermissionRequestPage&gt;
49 86 void initState() {
50 87 super.initState();
51 88 WidgetsBinding.instance.addObserver(this);
  89 + String permissionTypeStrs = widget.permissionTypeStrs.join('、');
52 90 msgList = [
53   - "${widget.permissionTypeStr}功能需要获取您设备的${widget.permissionTypeStr}权限,否则可能无法正常工作。\n是否申请${widget.permissionTypeStr}权限?",
54   - "${widget.permissionTypeStr}权限不全,是否重新申请权限?",
55   - "没有${widget.permissionTypeStr}权限,您可以手动开启权限",
  91 + "Wow English需要获取您设备的$permissionTypeStrs权限,否则可能无法正常工作。\n是否同意授予?",
  92 + "$permissionTypeStrs权限不全,是否重新申请权限?",
  93 + "没有$permissionTypeStrs权限,您可以手动开启权限",
56 94 widget.isRequiredPermission ? "退出应用" : "取消"
57 95 ];
58   - _checkPermission(widget.permission);
  96 + _handlePermission(widget.permissions);
59 97 }
60 98  
61 99 @override
... ... @@ -64,35 +102,63 @@ class _PermissionRequestPageState extends State&lt;PermissionRequestPage&gt;
64 102 Log.d("didChangeAppLifecycleState state=$state _isGoSetting=$_isGoSetting");
65 103 // 监听 app 从后台切回前台
66 104 if (state == AppLifecycleState.resumed && _isGoSetting) {
67   - _checkPermission(widget.permission);
  105 + _handlePermission(widget.permissions);
68 106 }
69 107 }
70 108  
71 109 /// 校验权限
72   - void _checkPermission(Permission permission) async {
73   - final PermissionStatus status = await permission.status;
74   - _handlePermissionStatus(permission, status);
  110 + void _handlePermission(List<Permission> permissions) async {
  111 + ///一个新待申请权限列表
  112 + List<Permission> intentPermissionList = [];
  113 +
  114 + ///遍历当前权限申请列表
  115 + for (Permission permission in permissions) {
  116 + PermissionStatus status = await permission.status;
  117 +
  118 + ///如果不是允许状态就添加到新的申请列表中
  119 + if (!status.isGranted) {
  120 + intentPermissionList.add(permission);
  121 + }
  122 + }
  123 +
  124 + if (intentPermissionList.isEmpty) {
  125 + _popPage(true);
  126 + } else {
  127 + _requestPermission(intentPermissionList);
  128 + }
75 129 }
76 130  
77   - void _handlePermissionStatus(Permission permission, PermissionStatus status) {
78   - Log.d('_handlePermissionStatus=$status permission=$permission');
79   - if (status.isGranted) {
80   - _popPage();
  131 + ///实际触发请求权限
  132 + Future<void> _requestPermission(List<Permission> permissions) async {
  133 + Log.d('_requestPermission permissions=$permissions');
  134 + if (await isPermissionsGranted(permissions)) {
  135 + _popPage(true);
81 136 return;
82 137 }
83 138  
  139 + MapEntry<Permission, PermissionStatus>? statusEntry = await requestPermissions(permissions);
  140 + if (statusEntry == null) {
  141 + ///都手动同意授予了
  142 + _popPage(true);
  143 + return;
  144 + }
  145 +
  146 + Permission permission = statusEntry.key;
  147 + PermissionStatus status = statusEntry.value;
84 148 // 还未申请权限或之前拒绝了权限(在 iOS 上为首次申请权限,拒绝后将变为 `永久拒绝权限`)
85 149 if (status.isDenied) {
86 150 showAlert(
87 151 permission, msgList[0], msgList[3], _isGoSetting ? "前往系统设置" : "确定");
88 152 }
89 153 // 权限已被永久拒绝
  154 + /// 在 Android 上:Android 11+ (API 30+):用户是否第二次拒绝权限。低于 Android 11 (API 30):用户是否拒绝访问请求的功能,并选择不再显示请求。
  155 + /// 在 iOS 上:如果用户拒绝访问所请求的功能。
90 156 if (status.isPermanentlyDenied) {
91 157 _isGoSetting = true;
92 158 showAlert(
93 159 permission, msgList[2], msgList[3], _isGoSetting ? "前往系统设置" : "确定");
94 160 }
95   - // 拥有部分权限
  161 + // 拥有部分权限(受限,仅在 iOS (iOS14+) 上受支持)
96 162 if (status.isLimited) {
97 163 if (Platform.isIOS || Platform.isMacOS) _isGoSetting = true;
98 164 showAlert(
... ... @@ -126,7 +192,7 @@ class _PermissionRequestPageState extends State&lt;PermissionRequestPage&gt;
126 192 onPressed: () {
127 193 widget.isRequiredPermission
128 194 ? _quitApp()
129   - : _popDialogAndPage(context);
  195 + : _popDialogAndPage(context, false);
130 196 }),
131 197 TextButton(
132 198 child: Text(confirmMsg),
... ... @@ -134,7 +200,7 @@ class _PermissionRequestPageState extends State&lt;PermissionRequestPage&gt;
134 200 if (_isGoSetting) {
135 201 openAppSettings();
136 202 } else {
137   - _requestPermisson(permission);
  203 + _handlePermission(widget.permissions);
138 204 }
139 205 _popDialog(context);
140 206 })
... ... @@ -145,15 +211,6 @@ class _PermissionRequestPageState extends State&lt;PermissionRequestPage&gt;
145 211 });
146 212 }
147 213  
148   - /// 申请权限
149   - void _requestPermisson(Permission permission) async {
150   - // 申请权限
151   - PermissionStatus status = await permission.request();
152   - Log.d('requestPermisson权限检测=$status _isGoSetting=$_isGoSetting');
153   - // 再次校验
154   - _handlePermissionStatus(permission, status);
155   - }
156   -
157 214 @override
158 215 void dispose() {
159 216 WidgetsBinding.instance.removeObserver(this);
... ... @@ -171,9 +228,9 @@ class _PermissionRequestPageState extends State&lt;PermissionRequestPage&gt;
171 228 }
172 229  
173 230 /// 关闭整个权限申请页面
174   - void _popDialogAndPage(BuildContext dialogContext) {
  231 + void _popDialogAndPage(BuildContext dialogContext, bool isAllGranted) {
175 232 _popDialog(dialogContext);
176   - _popPage();
  233 + _popPage(isAllGranted);
177 234 }
178 235  
179 236 /// 关闭弹窗
... ... @@ -182,7 +239,7 @@ class _PermissionRequestPageState extends State&lt;PermissionRequestPage&gt;
182 239 }
183 240  
184 241 /// 关闭透明页面
185   - void _popPage() {
186   - Navigator.of(context).pop();
  242 + void _popPage(bool isAllGranted) {
  243 + Navigator.of(context).pop(isAllGranted);
187 244 }
188 245 }
... ...
lib/pages/user/modify/modify_user_avatar_page.dart
... ... @@ -22,7 +22,7 @@ class ModifyUserAvatarPage extends StatelessWidget {
22 22 @override
23 23 Widget build(BuildContext context) {
24 24 return BlocProvider(
25   - create: (context) => UserAvatarBloc(),
  25 + create: (context) => UserAvatarBloc(context),
26 26 child: _ModifyUserAvatarPage(pageType: pageType),
27 27 );
28 28 }
... ...
lib/pages/user/modify/user_avatar_bloc/user_avatar_bloc.dart
... ... @@ -14,6 +14,8 @@ import &#39;package:wow_english/utils/aliyun_oss_util.dart&#39;;
14 14 import 'package:wow_english/utils/log_util.dart';
15 15 import 'package:wow_english/utils/toast_util.dart';
16 16  
  17 +import '../../../../common/permission/permissionRequestPage.dart';
  18 +
17 19 part 'user_avatar_event.dart';
18 20 part 'user_avatar_state.dart';
19 21  
... ... @@ -32,7 +34,9 @@ class UserAvatarBloc extends Bloc&lt;UserAvatarEvent, UserAvatarState&gt; {
32 34  
33 35 final ImagePicker picker = ImagePicker();
34 36  
35   - UserAvatarBloc() : super(UserAvatarInitial()) {
  37 + final BuildContext context;
  38 +
  39 + UserAvatarBloc(this.context) : super(UserAvatarInitial()) {
36 40 on<GetImageFromPhotoEvent>(_getImageFromPhoto);
37 41 on<GetImageFromCameraEvent>(_getImageFromCamera);
38 42 on<ChangeUserEnterAppStateEvent>(_changeUserEnterAppState);
... ... @@ -75,11 +79,8 @@ class UserAvatarBloc extends Bloc&lt;UserAvatarEvent, UserAvatarState&gt; {
75 79 }
76 80  
77 81 void _getImageFromCamera(GetImageFromCameraEvent event, Emitter<UserAvatarState> emitter) async {
78   - await getPermissionStatus(Permission.camera).then((value) async {
79   - if (!value) {
80   - debugPrint('失败$value');
81   - return;
82   - }
  82 + bool result = await permissionCheckAndRequest(context, Permission.camera, "拍照");
  83 + if (result) {
83 84 _file = await picker.pickImage(source: ImageSource.camera);
84 85 EasyLoading.show();
85 86 try {
... ... @@ -90,7 +91,23 @@ class UserAvatarBloc extends Bloc&lt;UserAvatarEvent, UserAvatarState&gt; {
90 91 showToast('上传头像失败: $e');
91 92 }
92 93 EasyLoading.dismiss();
93   - });
  94 + }
  95 + // await getPermissionStatus(Permission.camera).then((value) async {
  96 + // if (!value) {
  97 + // debugPrint('失败$value');
  98 + // return;
  99 + // }
  100 + // _file = await picker.pickImage(source: ImageSource.camera);
  101 + // EasyLoading.show();
  102 + // try {
  103 + // final urlStr = await _uploadAvatar(_file!.path);
  104 + // emitter(ChangeImageState(urlStr));
  105 + // } catch (e) {
  106 + // Log.e('上传头像失败:$e');
  107 + // showToast('上传头像失败: $e');
  108 + // }
  109 + // EasyLoading.dismiss();
  110 + // });
94 111 }
95 112  
96 113 void _changeUserEnterAppState(ChangeUserEnterAppStateEvent event, Emitter<UserAvatarState> emitter) async {
... ...