The issue you’re experiencing is likely related to changes in Android’s permission model starting from Android 10 (API level 29) and the deprecation of WRITE_EXTERNAL_STORAGE
for scoped storage. Also, newer Android versions manage permissions differently, especially around storage access.
Here’s how to resolve this issue and correctly request storage permissions in your Flutter app:
Step 1: Modify AndroidManifest.xml
Starting from Android 11 (API level 30), Android introduced scoped storage. To request legacy storage access, you need to include the following in your AndroidManifest.xml
:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.app">
<application
android:requestLegacyExternalStorage="true"
android:label="My App"
android:icon="@mipmap/ic_launcher">
...
</application>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- This permission is required for Android 10 (API level 29) and above -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage" />
</manifest>
Note:
android:requestLegacyExternalStorage="true"
ensures that your app continues to have access to external storage in the legacy way (without scoped storage) on devices running Android 10 (API level 29).
android.permission.MANAGE_EXTERNAL_STORAGE
is needed on Android 11+ for full access to external storage. However, this permission only works if you redirect users to the settings page where they manually grant the permission.
Step 2: Update Your permission_handler
Request
You’ll need to handle storage permissions depending on the Android version.
In Android 10 (API level 29) and below, you can use READ_EXTERNAL_STORAGE
and WRITE_EXTERNAL_STORAGE
. For Android 11+, use MANAGE_EXTERNAL_STORAGE
.
You can update your button’s onPressed
function as follows:
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Storage Permission')),
body: Center(
child: ElevatedButton(
onPressed: () async {
if (Platform.isAndroid) {
// For Android 11 and above
if (await _requestStoragePermission()) {
print("Storage permission granted.");
} else {
print("Storage permission denied.");
}
}
},
child: Text('Ask Permission'),
),
),
),
);
}
Future<bool> _requestStoragePermission() async {
if (Platform.isAndroid && await _isAndroid11OrAbove()) {
// Check if the app has manage external storage permission
PermissionStatus status = await Permission.manageExternalStorage.status;
if (status != PermissionStatus.granted) {
// Request the manage external storage permission for Android 11+
return await Permission.manageExternalStorage.request().isGranted;
}
} else {
// For Android 10 and below
PermissionStatus status = await Permission.storage.request();
if (status == PermissionStatus.granted) {
return true;
}
}
return false;
}
// Check if the device is Android 11 or higher
Future<bool> _isAndroid11OrAbove() async {
final version = await Permission.storage.status;
return version.isGranted && Platform.isAndroid && Platform.version >= 30;
}
}
Step 3: Handle Android 11+ Scoped Storage
Starting from Android 11, apps cannot request WRITE_EXTERNAL_STORAGE
the same way they did in previous versions. Instead, Android encourages apps to use the Scoped Storage model, which limits how apps can access shared storage.
If your app needs full access to the device’s storage (for example, reading/writing files anywhere on external storage), you need to request MANAGE_EXTERNAL_STORAGE
permission as shown in the code above. This will redirect the user to the settings page where they have to manually grant this permission.
However, for most apps, it’s better to use Scoped Storage, which provides more granular and privacy-friendly access to files.
Step 4: Test on Devices
Ensure you test your app on multiple devices, including:
- Android 10 (API level 29) or lower for legacy storage access.
- Android 11 (API level 30) or higher for scoped storage behavior.
Step 5: Flutter Clean
After making these changes, don’t forget to run the following commands to ensure everything is properly reset:
flutter clean
flutter pub get
Finally
Android’s storage permission model has changed significantly over recent versions, and it’s important to ensure that you’re requesting the correct permissions for the API level your app is targeting. By adding the MANAGE_EXTERNAL_STORAGE
permission for Android 11+ and using requestLegacyExternalStorage="true"
for Android 10, you should be able to resolve the issue with the “No Permission Required” settings page.