Flutter Android13如何打开非音视频图片类附件文件?
问题:Android 13 打开PDF附件时权限报错
- 需求:需打开应用内的各类附件文件,涵盖图片、视频、PDF、Excel等格式
- 权限处理:针对Android 13,使用
READ_MEDIA_IMAGES、READ_MEDIA_VIDEO、READ_MEDIA_AUDIO权限,通过permission_handler包请求:[Permission.photos, Permission.videos, Permission.audio].request() - 文件打开:通过open_file包从通知中打开已下载文件,调用方法:
OpenFile.open(path) - 异常情况:打开PDF文件时失败,报错信息:Permission denied: android.permission.MANAGE_EXTERNAL_STORAGE flutter。该权限在设置中已被永久拒绝,引导用户跳转设置操作不直观,且添加该权限会导致应用被Play Store拒绝。
相关代码
Flutter 代码
import 'dart:io'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:open_file/open_file.dart'; import 'package:pagedesk/data/model/ticket/attachemnt_item.dart'; import 'package:pagedesk/data/network/firebase_api.dart'; import 'package:pagedesk/utils/Utils.dart'; import 'package:pagedesk/view_model/ticket_attachemnts_view_model.dart'; import 'package:path_provider/path_provider.dart'; import 'package:permission_handler/permission_handler.dart'; class Notifications { int maxProgress = 5; bool isCompleted = false; Future getDownloadNotification( AttachemntItem item, String downloadingText, String downloadCompletedText, String downoadFaildMessage, String cantOpenFailMessage, TicketAttachemntsViewModel viewModel) async { final AndroidNotificationDetails androidNotificationDetails = AndroidNotificationDetails( 'name', 'name', channelDescription: 'progress channel description', channelShowBadge: false, importance: Importance.max, priority: Priority.high, onlyAlertOnce: true, showProgress: false, ); final NotificationDetails notificationDetails = NotificationDetails(android: androidNotificationDetails); String newPath = ""; Directory directory; try { if (Platform.isAndroid) { if (await requestStoragePermissions()) { directory = (await getExternalStorageDirectory())!; print(directory); List<String> paths = directory.path.split("/"); for (int x = 1; x < paths.length; x++) { String folder = paths[x]; if (folder != "Android") { newPath += "/$folder"; } else { break; } } newPath = "$newPath/Download"; directory = Directory(newPath); } else { return; } } else { if (await _requestPermission(Permission.storage)) { directory = await getApplicationDocumentsDirectory(); } else { return; } } if (!await directory.exists()) { await directory.create(recursive: true); } String filename = item.name; String path = directory.path; print('FILENAME: $filename'); print('OATH: $path'); File file = File('$path/$filename'); if (await file.exists()) { int suffix = 1; String newFileName; while (await file.exists()) { newFileName = '${filename.replaceAll(RegExp(r'\..+'), '')}($suffix)${filename.substring(filename.lastIndexOf('.'))}'; file = File('$path/$newFileName'); suffix++; } } else { print("File doesn't exist"); } FirebaseApi.localNotifications.show( item.id, item.name, isCompleted ? downloadCompletedText : downloadingText, notificationDetails, payload: newPath); viewModel.getAttachment(item).then((value) => { if (value != null) { file.writeAsBytes(value), isCompleted = true, FirebaseApi.localNotifications.cancel(item.id), FirebaseApi.localNotifications.show(item.id, item.name, downloadCompletedText, notificationDetails, payload: file.path) } else { isCompleted = false, FirebaseApi.localNotifications.cancel(item.id), FirebaseApi.localNotifications.show(item.id, item.name, downoadFaildMessage, notificationDetails, payload: null) } }); } catch (e) { print('ERROR'); } } } void openFile(String path, String cantOpenFileMessage) async { try { final result = await OpenFile.open(path); if (result.type == ResultType.done) { print('File opened successfully'); } else if (result.type == ResultType.noAppToOpen) { Utils.toastMessage(cantOpenFileMessage); } else { Utils.toastMessage("Can't open file throgh this app."); } } catch (e) { print('Error opening file: $e'); } } Future<bool> _requestPermission(Permission permission) async { if (await permission.isGranted) { return true; } else if (await permission.isPermanentlyDenied) { openAppSettings(); return false; } else { var result = await permission.request(); if (result == PermissionStatus.granted) { return true; } } return false; } Future<bool> requestStoragePermissions() async { List<Permission> permissions = []; final deviceInfo = await DeviceInfoPlugin().androidInfo; if (deviceInfo.version.sdkInt > 32) { permissions = [Permission.photos, Permission.videos, Permission.audio]; } else { permissions = [Permission.storage]; } Map<Permission, PermissionStatus> statuses = await permissions.request(); return !statuses.containsKey(false); }
AndroidManifest.xml 权限配置
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" /> <uses-permission android:name="android.permission. READ_MEDIA_AUDIO" />
内容的提问来源于stack exchange,提问作者Dušan Perić
相关产品推荐
相关产品推荐

