Commit 1daca20d5640a9c1ca1059dd67d2d5f74e44808a

Authored by 吴启风
1 parent 1f5969b8

pref:权限申请页面优化

lib/common/permission/permissionRequestPage.dart
  1 +import 'dart:async';
1 import 'dart:io'; 2 import 'dart:io';
2 import 'package:flutter/material.dart'; 3 import 'package:flutter/material.dart';
  4 +import 'package:package_info_plus/package_info_plus.dart';
3 import 'package:permission_handler/permission_handler.dart'; 5 import 'package:permission_handler/permission_handler.dart';
4 import 'package:wow_english/common/core/app_config_helper.dart'; 6 import 'package:wow_english/common/core/app_config_helper.dart';
5 7
6 import '../../utils/log_util.dart'; 8 import '../../utils/log_util.dart';
7 9
8 -/// 权限检查及请求 10 +/// 带隐私合规功能的权限检查及请求
9 /// 外部可通过此方法来进行权限的检查和请求,将自动跳转到`PermissionRequestPage`页面。 11 /// 外部可通过此方法来进行权限的检查和请求,将自动跳转到`PermissionRequestPage`页面。
10 /// 传入 `Permission` 以及对应的权限名称 `permissionTypeStr`,如果有权限则返回 `Future true` 12 /// 传入 `Permission` 以及对应的权限名称 `permissionTypeStr`,如果有权限则返回 `Future true`
11 /// `isRequiredPermission` 如果为 `true`,则 "取消" 按钮将执行 "退出app" 的操作 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 Future<bool> permissionCheckAndRequest( 38 Future<bool> permissionCheckAndRequest(
13 BuildContext context, 39 BuildContext context,
14 Permission permission, 40 Permission permission,
15 String permissionTypeStr, 41 String permissionTypeStr,
16 {bool isRequiredPermission = false}) async { 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 class PermissionRequestPage extends StatefulWidget { 66 class PermissionRequestPage extends StatefulWidget {
31 - const PermissionRequestPage(this.permission, this.permissionTypeStr, 67 + const PermissionRequestPage(this.permissions, this.permissionTypeStrs,
32 {super.key, this.isRequiredPermission = false}); 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 final bool isRequiredPermission; 73 final bool isRequiredPermission;
37 74
38 @override 75 @override
@@ -49,13 +86,14 @@ class _PermissionRequestPageState extends State&lt;PermissionRequestPage&gt; @@ -49,13 +86,14 @@ class _PermissionRequestPageState extends State&lt;PermissionRequestPage&gt;
49 void initState() { 86 void initState() {
50 super.initState(); 87 super.initState();
51 WidgetsBinding.instance.addObserver(this); 88 WidgetsBinding.instance.addObserver(this);
  89 + String permissionTypeStrs = widget.permissionTypeStrs.join('、');
52 msgList = [ 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 widget.isRequiredPermission ? "退出应用" : "取消" 94 widget.isRequiredPermission ? "退出应用" : "取消"
57 ]; 95 ];
58 - _checkPermission(widget.permission); 96 + _handlePermission(widget.permissions);
59 } 97 }
60 98
61 @override 99 @override
@@ -64,35 +102,63 @@ class _PermissionRequestPageState extends State&lt;PermissionRequestPage&gt; @@ -64,35 +102,63 @@ class _PermissionRequestPageState extends State&lt;PermissionRequestPage&gt;
64 Log.d("didChangeAppLifecycleState state=$state _isGoSetting=$_isGoSetting"); 102 Log.d("didChangeAppLifecycleState state=$state _isGoSetting=$_isGoSetting");
65 // 监听 app 从后台切回前台 103 // 监听 app 从后台切回前台
66 if (state == AppLifecycleState.resumed && _isGoSetting) { 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 return; 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 // 还未申请权限或之前拒绝了权限(在 iOS 上为首次申请权限,拒绝后将变为 `永久拒绝权限`) 148 // 还未申请权限或之前拒绝了权限(在 iOS 上为首次申请权限,拒绝后将变为 `永久拒绝权限`)
85 if (status.isDenied) { 149 if (status.isDenied) {
86 showAlert( 150 showAlert(
87 permission, msgList[0], msgList[3], _isGoSetting ? "前往系统设置" : "确定"); 151 permission, msgList[0], msgList[3], _isGoSetting ? "前往系统设置" : "确定");
88 } 152 }
89 // 权限已被永久拒绝 153 // 权限已被永久拒绝
  154 + /// 在 Android 上:Android 11+ (API 30+):用户是否第二次拒绝权限。低于 Android 11 (API 30):用户是否拒绝访问请求的功能,并选择不再显示请求。
  155 + /// 在 iOS 上:如果用户拒绝访问所请求的功能。
90 if (status.isPermanentlyDenied) { 156 if (status.isPermanentlyDenied) {
91 _isGoSetting = true; 157 _isGoSetting = true;
92 showAlert( 158 showAlert(
93 permission, msgList[2], msgList[3], _isGoSetting ? "前往系统设置" : "确定"); 159 permission, msgList[2], msgList[3], _isGoSetting ? "前往系统设置" : "确定");
94 } 160 }
95 - // 拥有部分权限 161 + // 拥有部分权限(受限,仅在 iOS (iOS14+) 上受支持)
96 if (status.isLimited) { 162 if (status.isLimited) {
97 if (Platform.isIOS || Platform.isMacOS) _isGoSetting = true; 163 if (Platform.isIOS || Platform.isMacOS) _isGoSetting = true;
98 showAlert( 164 showAlert(
@@ -126,7 +192,7 @@ class _PermissionRequestPageState extends State&lt;PermissionRequestPage&gt; @@ -126,7 +192,7 @@ class _PermissionRequestPageState extends State&lt;PermissionRequestPage&gt;
126 onPressed: () { 192 onPressed: () {
127 widget.isRequiredPermission 193 widget.isRequiredPermission
128 ? _quitApp() 194 ? _quitApp()
129 - : _popDialogAndPage(context); 195 + : _popDialogAndPage(context, false);
130 }), 196 }),
131 TextButton( 197 TextButton(
132 child: Text(confirmMsg), 198 child: Text(confirmMsg),
@@ -134,7 +200,7 @@ class _PermissionRequestPageState extends State&lt;PermissionRequestPage&gt; @@ -134,7 +200,7 @@ class _PermissionRequestPageState extends State&lt;PermissionRequestPage&gt;
134 if (_isGoSetting) { 200 if (_isGoSetting) {
135 openAppSettings(); 201 openAppSettings();
136 } else { 202 } else {
137 - _requestPermisson(permission); 203 + _handlePermission(widget.permissions);
138 } 204 }
139 _popDialog(context); 205 _popDialog(context);
140 }) 206 })
@@ -145,15 +211,6 @@ class _PermissionRequestPageState extends State&lt;PermissionRequestPage&gt; @@ -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 @override 214 @override
158 void dispose() { 215 void dispose() {
159 WidgetsBinding.instance.removeObserver(this); 216 WidgetsBinding.instance.removeObserver(this);
@@ -171,9 +228,9 @@ class _PermissionRequestPageState extends State&lt;PermissionRequestPage&gt; @@ -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 _popDialog(dialogContext); 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,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,7 +22,7 @@ class ModifyUserAvatarPage extends StatelessWidget {
22 @override 22 @override
23 Widget build(BuildContext context) { 23 Widget build(BuildContext context) {
24 return BlocProvider( 24 return BlocProvider(
25 - create: (context) => UserAvatarBloc(), 25 + create: (context) => UserAvatarBloc(context),
26 child: _ModifyUserAvatarPage(pageType: pageType), 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,6 +14,8 @@ import &#39;package:wow_english/utils/aliyun_oss_util.dart&#39;;
14 import 'package:wow_english/utils/log_util.dart'; 14 import 'package:wow_english/utils/log_util.dart';
15 import 'package:wow_english/utils/toast_util.dart'; 15 import 'package:wow_english/utils/toast_util.dart';
16 16
  17 +import '../../../../common/permission/permissionRequestPage.dart';
  18 +
17 part 'user_avatar_event.dart'; 19 part 'user_avatar_event.dart';
18 part 'user_avatar_state.dart'; 20 part 'user_avatar_state.dart';
19 21
@@ -32,7 +34,9 @@ class UserAvatarBloc extends Bloc&lt;UserAvatarEvent, UserAvatarState&gt; { @@ -32,7 +34,9 @@ class UserAvatarBloc extends Bloc&lt;UserAvatarEvent, UserAvatarState&gt; {
32 34
33 final ImagePicker picker = ImagePicker(); 35 final ImagePicker picker = ImagePicker();
34 36
35 - UserAvatarBloc() : super(UserAvatarInitial()) { 37 + final BuildContext context;
  38 +
  39 + UserAvatarBloc(this.context) : super(UserAvatarInitial()) {
36 on<GetImageFromPhotoEvent>(_getImageFromPhoto); 40 on<GetImageFromPhotoEvent>(_getImageFromPhoto);
37 on<GetImageFromCameraEvent>(_getImageFromCamera); 41 on<GetImageFromCameraEvent>(_getImageFromCamera);
38 on<ChangeUserEnterAppStateEvent>(_changeUserEnterAppState); 42 on<ChangeUserEnterAppStateEvent>(_changeUserEnterAppState);
@@ -75,11 +79,8 @@ class UserAvatarBloc extends Bloc&lt;UserAvatarEvent, UserAvatarState&gt; { @@ -75,11 +79,8 @@ class UserAvatarBloc extends Bloc&lt;UserAvatarEvent, UserAvatarState&gt; {
75 } 79 }
76 80
77 void _getImageFromCamera(GetImageFromCameraEvent event, Emitter<UserAvatarState> emitter) async { 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 _file = await picker.pickImage(source: ImageSource.camera); 84 _file = await picker.pickImage(source: ImageSource.camera);
84 EasyLoading.show(); 85 EasyLoading.show();
85 try { 86 try {
@@ -90,7 +91,23 @@ class UserAvatarBloc extends Bloc&lt;UserAvatarEvent, UserAvatarState&gt; { @@ -90,7 +91,23 @@ class UserAvatarBloc extends Bloc&lt;UserAvatarEvent, UserAvatarState&gt; {
90 showToast('上传头像失败: $e'); 91 showToast('上传头像失败: $e');
91 } 92 }
92 EasyLoading.dismiss(); 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 void _changeUserEnterAppState(ChangeUserEnterAppStateEvent event, Emitter<UserAvatarState> emitter) async { 113 void _changeUserEnterAppState(ChangeUserEnterAppStateEvent event, Emitter<UserAvatarState> emitter) async {