Android 10与Android 11系统下设置外部存储或自定义地图下载路径的方法(Flutter环境)
Hey there, let's break down how to properly configure a custom external or internal storage path for map downloads in your Flutter project, specifically targeting Android 10 (API 29) and Android 11 (API 30). The key here is adapting to Android's Scoped Storage system introduced in Android 10, which changes how apps access external storage.
First: Understand Scoped Storage Implications
Android 10+ restricts direct access to most external storage locations to protect user privacy. There are two main paths you can use, each with different permission requirements:
- App-specific external storage: This is a private directory for your app (e.g.,
Android/data/com.your.app/files/). No extra permissions needed, and data is deleted when your app is uninstalled. This is the recommended approach. - Public storage directories: Like the Downloads or Documents folder. Requires the
MANAGE_EXTERNAL_STORAGEpermission on Android 11+, and users have to manually grant access via system settings. Only use this if users need direct access to map files outside your app.
Step 1: Add Required Dependencies
To handle storage paths and permissions in Flutter, add these packages to your pubspec.yaml:
dependencies: flutter: sdk: flutter path_provider: ^2.0.15 # For accessing storage directories permission_handler: ^10.2.0 # For requesting permissions
Step 2: Get a Valid Storage Path
Option 1: App-Specific External Storage (Recommended)
This path requires no extra permissions and is compliant with Android 10+ rules:
import 'dart:io'; import 'package:path_provider/path_provider.dart'; Future<String> getAppSpecificMapPath() async { // Get the app's private external storage directory Directory? externalDir = await getExternalStorageDirectory(); if (externalDir == null) throw Exception("Failed to access external storage"); // Create a subdirectory for map files (ensure it exists) final mapDir = Directory('${externalDir.path}/MapCache'); await mapDir.create(recursive: true); return mapDir.path; }
Option 2: Public Storage Directory (Android 11+)
If you need to use a public folder like Downloads, you'll need to request the MANAGE_EXTERNAL_STORAGE permission:
import 'dart:io'; import 'package:path_provider/path_provider.dart'; import 'package:permission_handler/permission_handler.dart'; Future<String> getPublicMapPath() async { // Check and request MANAGE_EXTERNAL_STORAGE permission var permissionStatus = await Permission.manageExternalStorage.status; if (!permissionStatus.isGranted) { permissionStatus = await Permission.manageExternalStorage.request(); if (!permissionStatus.isGranted) { // If permission is denied, redirect user to app settings await openAppSettings(); throw Exception("Storage permission is required to use this path"); } } // Get the public Downloads directory Directory? publicDir = await getExternalStoragePublicDirectory(StorageDirectory.downloads); if (publicDir == null) throw Exception("Failed to access public storage"); // Create a subdirectory for maps final mapDir = Directory('${publicDir.path}/MyCustomMaps'); await mapDir.create(recursive: true); return mapDir.path; }
Step 3: Configure Your SDKOptions Correctly
Once you have a valid path, pass it to your SDKOptions initialization. Make sure the directory exists before initializing the SDK (the code above handles this with create(recursive: true)):
void initMapSDK() async { try { // Choose either the app-specific or public path String externalPath = await getAppSpecificMapPath(); // String externalPath = await getPublicMapPath(); SDKOptions sdkOptions = SDKOptions.withAccessKeySecretAndCachePathCacheSizeAndPersistentMapPath( sKey, sSecret, externalPath, ConfigurationSingleton.instance.cacheSizeBytes, externalPath ); // Initialize your map SDK with these options // ... (SDK initialization code here) } catch (e) { print("Map SDK initialization failed: $e"); } }
Step 4: Update AndroidManifest.xml
- For app-specific storage: No extra permissions needed, but you can add this for backward compatibility with Android 9 and below:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" /> - For public storage: Add the
MANAGE_EXTERNAL_STORAGEpermission for Android 11+:<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" android:minSdkVersion="30" />
Important Notes
- Avoid using
android:requestLegacyExternalStorage="true"in yourAndroidManifest.xml(a workaround for Android 10). This is deprecated in Android 12+ and won't be supported long-term. - Always test on real Android 10/11 devices—emulators may have different storage behavior.
- Ensure your map SDK is updated to the latest version, as older SDKs may not fully support Scoped Storage.
内容的提问来源于stack exchange,提问作者Benjamin Hernández




