You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter:无需MANAGE_EXTERNAL_STORAGE权限删除文件并更新MediaStore

解决方案:无需MANAGE_EXTERNAL_STORAGE权限删除音乐文件并更新MediaStore

在Android 10(API 29)及以上的Scoped Storage机制下,直接通过File.deleteSync()操作公共目录(如Download)的文件会受到权限限制,尤其是其他应用创建的文件。要在无MANAGE_EXTERNAL_STORAGE权限的前提下完成删除并同步MediaStore,需通过MediaStore ContentResolver或用户授权的URI来操作,以下是具体实现方案:


方案1:通过MediaStore删除已索引的音乐文件(适用于自身应用创建的文件)

如果目标文件已被MediaStore索引(多数音乐文件会自动被索引),可以通过查询MediaStore获取文件对应的URI,再通过ContentResolver执行删除操作。

Flutter端代码

import 'package:flutter/services.dart';
import 'package:permission_handler/permission_handler.dart';

// 定义MethodChannel与原生通信
const _mediaStoreChannel = MethodChannel('your_app_id/media_store');

Future<void> deleteMusicFile(String filePath, BuildContext context, int index) async {
  log('Target file path: $filePath');
  try {
    // 适配Android 10以下的存储权限
    if (await Permission.storage.request().isGranted) {
      final bool deleteSuccess = await _mediaStoreChannel.invokeMethod(
        'deleteMediaByPath',
        {'filePath': filePath},
      );

      if (deleteSuccess) {
        // 更新本地播放器状态
        final playerState = Provider.of<PlayerStateles>(context, listen: false);
        playerState.updateCurrentIndex(index + 1);
        snackBarMessage(context);
      } else {
        log('Failed to delete file via MediaStore');
      }
    } else {
      log('Storage permission not granted');
    }
  } catch (e) {
    log('Delete error: ${e.toString()}');
  }
}

Android原生代码(Kotlin)

在MainActivity中注册MethodChannel并实现删除逻辑:

import android.content.ContentUris
import android.provider.MediaStore
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel

class MainActivity : FlutterActivity() {
    private val CHANNEL = "your_app_id/media_store"

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
            when (call.method) {
                "deleteMediaByPath" -> {
                    val filePath = call.argument<String>("filePath")
                    if (filePath != null) {
                        val success = deleteMediaFile(filePath)
                        result.success(success)
                    } else {
                        result.error("INVALID_ARG", "File path is null", null)
                    }
                }
                else -> result.notImplemented()
            }
        }
    }

    private fun deleteMediaFile(filePath: String): Boolean {
        // 查询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 mediaUri = ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, mediaId)
                // 执行删除操作
                return contentResolver.delete(mediaUri, null, null) > 0
            }
        }
        return false
    }
}

方案2:通过用户授权的URI删除文件(适用于其他应用创建的文件)

如果目标文件是其他应用创建的(如Download目录的第三方下载文件),Android 11+下无法直接通过路径删除,需引导用户通过系统文件选择器选择文件,获取持久URI权限后执行删除。

Flutter端代码

import 'package:file_picker/file_picker.dart';
import 'package:flutter/services.dart';

Future<void> pickAndDeleteMusic(BuildContext context, int index) async {
  // 启动音频文件选择器
  final pickResult = await FilePicker.platform.pickFiles(
    type: FileType.audio,
    allowMultiple: false,
  );

  if (pickResult != null && pickResult.files.single.uri != null) {
    final fileUri = pickResult.files.single.uri;
    // 请求持久URI权限
    await _mediaStoreChannel.invokeMethod(
      'requestPersistableUriPerm',
      {'uri': fileUri.toString()},
    );
    // 执行删除
    final deleteSuccess = await _mediaStoreChannel.invokeMethod(
      'deleteByUri',
      {'uri': fileUri.toString()},
    );

    if (deleteSuccess) {
      final playerState = Provider.of<PlayerStateles>(context, listen: false);
      playerState.updateCurrentIndex(index + 1);
      snackBarMessage(context);
    }
  }
}

Android原生代码扩展

在MainActivity的MethodCallHandler中添加以下逻辑:

override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
    super.configureFlutterEngine(flutterEngine)
    MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
        when (call.method) {
            // ... 保留方案1的deleteMediaByPath逻辑
            "requestPersistableUriPerm" -> {
                val uriStr = call.argument<String>("uri")
                if (uriStr != null) {
                    val uri = Uri.parse(uriStr)
                    val takeFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
                    contentResolver.takePersistableUriPermission(uri, takeFlags)
                    result.success(null)
                } else {
                    result.error("INVALID_ARG", "URI is null", null)
                }
            }
            "deleteByUri" -> {
                val uriStr = call.argument<String>("uri")
                if (uriStr != null) {
                    val uri = Uri.parse(uriStr)
                    val success = contentResolver.delete(uri, null, null) > 0
                    result.success(success)
                } else {
                    result.error("INVALID_ARG", "URI is null", null)
                }
            }
            else -> result.notImplemented()
        }
    }
}

关键说明

  1. MediaStore自动同步:通过ContentResolver删除文件后,MediaStore会自动更新索引,无需额外调用scanMedia方法。
  2. 权限适配:Android 10及以上无需WRITE_EXTERNAL_STORAGE权限即可操作MediaStore;Android 9及以下仍需申请该权限。
  3. 第三方文件限制:对于其他应用创建的文件,Android 11+强制要求用户主动选择文件并授权,无法静默删除。

内容的提问来源于stack exchange,提问作者yusuf

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 15:54:53