Android 11+ 环境下Flutter删除媒体文件的权限问题求解
Android 11+ 下Flutter实现媒体文件删除方案
由于Android 11及以上版本的**分区存储(Scoped Storage)**限制,直接通过File API删除公共目录下的媒体文件会失败,必须借助Android系统提供的MediaStore.createDeleteRequest()方法,让用户确认后由系统执行删除操作。以下是具体实现步骤:
一、核心思路
- 通过MethodChannel实现Flutter与原生Android的交互
- 原生端根据文件路径查询MediaStore对应的Content Uri
- 调用
createDeleteRequest()生成系统权限请求弹窗 - 用户确认后,系统自动完成文件删除,并将结果返回给Flutter端
二、Flutter端代码实现
1. 定义MethodChannel并实现删除调用
import 'package:flutter/services.dart'; class MediaDeleteManager { static const MethodChannel _channel = MethodChannel('media_delete_channel'); // 发起文件删除请求 static Future<void> requestDeleteFile(String filePath) async { try { await _channel.invokeMethod('deleteMediaFile', {'filePath': filePath}); } on PlatformException catch (e) { print('删除请求发起失败: ${e.message}'); } } // 监听删除结果 static void setupResultListener(Function(String) onResult) { _channel.setMethodCallHandler((call) async { if (call.method == 'deleteResult') { final String result = call.arguments as String; onResult(result); } return null; }); } }
2. 使用示例
// 在页面初始化时设置结果监听 @override void initState() { super.initState(); MediaDeleteManager.setupResultListener((result) { print('删除结果: $result'); // 这里可以更新UI提示用户,比如弹出SnackBar }); } // 触发删除操作 void handleDelete(String filePath) { MediaDeleteManager.requestDeleteFile(filePath); }
三、原生Android端代码实现
1. 在MainActivity中配置MethodChannel
import android.net.Uri import android.provider.MediaStore import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.IntentSenderRequest import androidx.activity.result.contract.ActivityResultContracts import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodChannel class MainActivity : FlutterActivity() { private val CHANNEL = "media_delete_channel" private lateinit var deleteResultLauncher: ActivityResultLauncher<IntentSenderRequest> override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) // 注册删除结果回调 deleteResultLauncher = registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { result -> val resultMsg = if (result.resultCode == RESULT_OK) "文件删除成功" else "用户取消删除" // 通知Flutter端结果 MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL) .invokeMethod("deleteResult", resultMsg) } // 设置MethodChannel处理逻辑 MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result -> when (call.method) { "deleteMediaFile" -> { val filePath = call.argument<String>("filePath") if (filePath.isNullOrEmpty()) { result.error("INVALID_PATH", "文件路径不能为空", null) return@setMethodCallHandler } deleteMediaFile(filePath, result) } else -> result.notImplemented() } } } private fun deleteMediaFile(filePath: String, result: MethodChannel.Result) { // 查询MediaStore中对应文件的Uri val projection = arrayOf(MediaStore.Audio.Media._ID) val selection = "${MediaStore.Audio.Media.DATA} = ?" val selectionArgs = arrayOf(filePath) contentResolver.query( MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, projection, selection, selectionArgs, null )?.use { cursor -> if (cursor.moveToFirst()) { val mediaId = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID)) val contentUri = ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, mediaId) // 创建系统删除请求 val deleteIntent = MediaStore.createDeleteRequest(contentResolver, listOf(contentUri)) // 启动权限请求弹窗 deleteResultLauncher.launch(IntentSenderRequest.Builder(deleteIntent.intentSender).build()) result.success("已发起删除请求,请等待用户确认") } else { result.error("FILE_NOT_FOUND", "未在媒体库中找到该文件", null) } } ?: result.error("QUERY_FAILED", "查询媒体库失败", null) } }
四、权限配置
在AndroidManifest.xml中添加必要权限:
<!-- Android 13及以上版本 --> <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" /> <!-- Android 11-12版本 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
使用permission_handler请求权限:
import 'package:permission_handler/permission_handler.dart'; Future<bool> requestAudioPermission() async { PermissionStatus status; if (await Permission.audio.isGranted) { return true; } status = await Permission.audio.request(); return status.isGranted; }
注意事项
- 仅能删除MediaStore中已收录的媒体文件,若为未被系统扫描到的文件,需先触发媒体库扫描
- 无需申请
WRITE_EXTERNAL_STORAGE或MANAGE_EXTERNAL_STORAGE权限,符合Google的合规要求 - 必须由用户手动确认删除操作,这是系统强制的隐私保护机制
内容的提问来源于stack exchange,提问作者user3221449
相关产品推荐
相关产品推荐

