I'm trying very hard to understand how to get the permission to write files in a selected directory with File Picker in Android. I'm also using permission handler package. The code is correct, I'm sure. It is something related to permissions in Android Manifest file (but I have already specified read and write external storage permissions).
The dialog in the app for allowing permissions doesn't appear. It directly opens me (as I intended in my code) the setting page to set manually the permission. But here, it is written that the app doesn't require any permission. If I try to write a file in the selected directory, the operation fails and the compiler says to me that this operation isn't allowed, something likes that. Thank you for any help.
The issue you're encountering when trying to save a file to a selected directory in Flutter for Android is due to changes in how Android handles storage permissions, especially starting from Android 10 (API level 29) and above. Even if you've added the read and write permissions in your AndroidManifest.xml, the app might not function as expected because of Scoped Storage and permission requirements.
Here's how you can resolve this issue:
Understanding Android Storage Permissions
Solution Steps
Ensure that your manifest file has the correct permissions and attributes.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.yourapp">
<!-- Permissions -->
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- For Android 10 and below -->
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28" />
<!-- For Android 11 and above (Optional, see notes below) -->
<uses-permission
android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<application
android:label="Your App"
android:icon="@mipmap/ic_launcher"
android:requestLegacyExternalStorage="true">
<!-- Other components -->
</application>
</manifest>
Notes:
android:requestLegacyExternalStorage="true" : Allows your app to use the old storage model on Android 10. This attribute is ignored on
Android 11 and above.Android 11 (API Level 30) and Above: Further restrictions were applied. The WRITE_EXTERNAL_STORAGE permission is deprecated, and apps are encouraged to use the Storage Access Framework (SAF) or request the MANAGE_EXTERNAL_STORAGE permission (which is highly restricted and requires special approval for Play Store apps).
Use the permission_handler package to request permissions dynamically.
import 'package:permission_handler/permission_handler.dart';
Future<void> requestStoragePermission() async {
var status = await Permission.storage.status;
if (!status.isGranted) {
// Request the permission
if (await Permission.storage.request().isGranted) {
// Permission granted
} else {
// Permission denied
// You can open app settings to let the user grant the permission
openAppSettings();
}
}
}
Important Always check and request permissions at runtime. Permissions declared in the manifest must also be granted by the user.
Starting from Android 11, it's recommended to use SAF to access files and directories outside of your app's specific storage.
You can use the storage_access_framework package to interact with SAF.
Example:
import 'package:storage_access_framework/storage_access_framework.dart';
Future<void> saveFile() async {
// Open directory picker
Uri? uri = await StorageAccessFramework.openDocumentTree();
if (uri == null) {
// User cancelled the picker
return;
}
// The file data you want to save
final fileName = 'example.txt';
final mimeType = 'text/plain';
final data = 'Hello, world!'.codeUnits;
try {
// Save the file in the selected directory
await StorageAccessFramework.createFileAsBytes(
uri,
fileName,
mimeType,
data,
);
print('File saved successfully!');
} catch (e) {
print('Error saving file: $e');
}
}
Benefits of Using SAF:
If your app doesn't request any dangerous permissions, the permissions might not appear in the app settings. Ensure you're requesting the permissions at runtime and handling the user's response appropriately.
Putting It All Together
Here's how you can implement the solution in your app:
Step 1: Update AndroidManifest.xml as shown above.
Step 2: Request storage permission (optional when using SAF, but good practice):
await requestStoragePermission();
Step 3: Use SAF to pick the directory and save the file:
await saveFile();
Full Example:
import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:storage_access_framework/storage_access_framework.dart';
class SaveFilePage extends StatefulWidget {
@override
_SaveFilePageState createState() => _SaveFilePageState();
}
class _SaveFilePageState extends State<SaveFilePage> {
Future<void> _saveFile() async {
// Request storage permission (optional)
if (await Permission.storage.request().isGranted) {
// Proceed with saving the file
await saveFile();
} else {
// Permission denied
// Optionally, guide the user to app settings
openAppSettings();
}
}
Future<void> saveFile() async {
// Open directory picker
Uri? uri = await StorageAccessFramework.openDocumentTree();
if (uri == null) {
// User cancelled the picker
return;
}
// The file data you want to save
final fileName = 'example.txt';
final mimeType = 'text/plain';
final data = 'Hello, world!'.codeUnits;
try {
// Save the file in the selected directory
await StorageAccessFramework.createFileAsBytes(
uri,
fileName,
mimeType,
data,
);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('File saved successfully!')),
);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error saving file: $e')),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Save File Example'),
),
body: Center(
child: ElevatedButton(
onPressed: _saveFile,
child: Text('Save File'),
),
));
}
}
Additional Tips
if (androidInfo.version.sdkInt >= 30) {
// Android 11 and above
// Use SAF
} else {
// Android 10 and below
// You might be able to use traditional methods
}
References
Conclusion
By updating your app to use the Storage Access Framework and handling permissions correctly, you should be able to save files to a user-selected directory without running into permission issues. This approach aligns with Android's storage policies and ensures your app is future-proof for newer Android versions.