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

Flutter不借助权限包请求外部存储权限遇权限拒绝报错

不依赖permission_handler包解决Flutter外部存储权限被拒问题

Android 平台处理步骤

1. 配置AndroidManifest.xml权限

在android/app/src/main/AndroidManifest.xml中添加对应权限声明:

<!-- Android 10及以下读写外部存储所需权限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

<!-- Android 11及以上访问全存储目录需添加(可选,需引导用户到设置手动开启) -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
    android:minSdkVersion="30" />

注意:Android 10(API 29)及以上启用了Scoped Storage机制,直接访问公共目录会受限制。若仅操作应用私有存储,无需申请WRITE_EXTERNAL_STORAGE;若需访问公共下载目录,可选择使用MANAGE_EXTERNAL_STORAGE权限(需引导用户到设置开启),或通过MediaStore API进行文件操作。

2. 通过MethodChannel实现动态权限请求

Flutter无内置权限API,不依赖三方包时需通过原生代码调用权限请求:

Flutter端代码

import 'package:flutter/services.dart';

class PermissionManager {
  static const MethodChannel _channel = MethodChannel('permission_channel');

  // 请求外部存储权限
  static Future<bool> requestStoragePermission() async {
    try {
      return await _channel.invokeMethod('requestStoragePermission');
    } on PlatformException catch (e) {
      print('权限请求失败: ${e.message}');
      return false;
    }
  }

  // 监听权限请求结果(需配合原生回调)
  static void listenPermissionResult(void Function(bool) onResult) {
    _channel.setMethodCallHandler((call) async {
      if (call.method == 'permissionResult') {
        onResult(call.arguments as bool);
      }
    });
  }
}

Android原生代码(Kotlin)

在MainActivity.kt中添加MethodChannel处理逻辑:

import android.Manifest
import android.content.pm.PackageManager
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel

class MainActivity : FlutterActivity() {
    private val CHANNEL = "permission_channel"
    private val REQUEST_STORAGE_PERMISSION = 1

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
            when (call.method) {
                "requestStoragePermission" -> {
                    val hasPermission = ContextCompat.checkSelfPermission(
                        this,
                        Manifest.permission.WRITE_EXTERNAL_STORAGE
                    ) == PackageManager.PERMISSION_GRANTED
                    if (!hasPermission) {
                        ActivityCompat.requestPermissions(
                            this,
                            arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE),
                            REQUEST_STORAGE_PERMISSION
                        )
                        result.success(false)
                    } else {
                        result.success(true)
                    }
                }
                else -> result.notImplemented()
            }
        }
    }

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == REQUEST_STORAGE_PERMISSION) {
            val isGranted = grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED
            MethodChannel(flutterEngine?.dartExecutor?.binaryMessenger, CHANNEL).invokeMethod("permissionResult", isGranted)
        }
    }
}

3. 正确获取存储路径

避免硬编码路径,改用系统API获取标准目录:

import 'package:path_provider/path_provider.dart';

Future<String> getDownloadPath() async {
  // 获取Android外部存储目录,拼接Download子目录
  final externalDir = await getExternalStorageDirectory();
  return "${externalDir?.path}/Download";
}

iOS 平台处理步骤

1. 配置Info.plist权限

在ios/Runner/Info.plist中添加权限说明:

<key>NSPhotoLibraryUsageDescription</key>
<string>需要访问相册存储文件</string>
<key>NSDocumentsFolderUsageDescription</key>
<string>需要访问文档目录存储文件</string>

2. 通过MethodChannel实现权限请求

Flutter端代码(复用前文的PermissionManager)

iOS原生代码(Swift)

在AppDelegate.swift中添加:

import UIKit
import Flutter
import Photos

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  private let CHANNEL = "permission_channel"

  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    let controller = window?.rootViewController as! FlutterViewController
    let channel = FlutterMethodChannel(name: CHANNEL, binaryMessenger: controller.binaryMessenger)
    
    channel.setMethodCallHandler { [weak self] call, result in
      guard call.method == "requestStoragePermission" else {
        result(FlutterMethodNotImplemented)
        return
      }
      self?.requestPhotoLibraryPermission(result: result)
    }

    GeneratedPluginRegistrant.register(with: self)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }

  private func requestPhotoLibraryPermission(result: FlutterResult) {
    PHPhotoLibrary.requestAuthorization { status in
      DispatchQueue.main.async {
        result(status == .authorized || status == .limited)
      }
    }
  }
}

最终使用流程

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  // 监听权限请求结果
  PermissionManager.listenPermissionResult((isGranted) {
    if (isGranted) {
      _handleFileOperation();
    } else {
      print("权限被拒绝,无法访问存储");
    }
  });

  // 先请求权限
  bool hasPermission = await PermissionManager.requestStoragePermission();
  if (hasPermission) {
    _handleFileOperation();
  }
}

Future<void> _handleFileOperation() async {
  String downloadPath = await getDownloadPath();
  File file = File("$downloadPath/events.json");
  // 执行读写文件逻辑
}

内容的提问来源于stack exchange,提问作者Карим Бозжанов

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 12:56:14