Search code examples
androidflutterdartfile-permissionsandroid-13

Storage Permission in Android 13 - Flutter Android 13


I'm developing a Flutter app that requires storage access on Android devices. The app functions properly on Android versions up to 12, but I'm encountering storage permission issues specifically on Android 13 devices. I've followed the standard implementation for requesting storage permissions and have included the necessary permission declarations in the app's AndroidManifest.xml file. However, on Android 13 devices, the app fails to access external storage and encounters permission errors.

  • Error
    I/ViewRootImpl@f9221bd[MainActivity](25559): handleAppVisibility mAppVisible = true visible = false
    I/SurfaceView@31e8ae4(25559): onWindowVisibilityChanged(8) false io.flutter.embedding.android.FlutterSurfaceView{31e8ae4 V.E...... ........ 0,0-720,1570} of ViewRootImpl@f9221bd[MainActivity]
    I/SurfaceView@31e8ae4(25559): Changes: creating=false format=false size=false visible=true alpha=false hint=false mUseAlpha=false visible=true left=false top=false
    I/SurfaceView@31e8ae4(25559): 52333284 Cur surface: Surface(name=null)/@0xfd2426a
    I/SurfaceView@31e8ae4(25559): applyTransactionOnVriDraw: vri = ViewRootImpl@f9221bd[MainActivity] fRS = false t = 0xb40000709aa32400 android.view.SurfaceView.performSurfaceTransaction:959 android.view.SurfaceView.updateSurface:1105 android.view.SurfaceView.onWindowVisibilityChanged:371 
    I/ViewRootImpl@f9221bd[MainActivity](25559): applyTransactionOnDraw: mRemoved = false isHardwareEnabled = true t = 0xb40000709aa32400android.view.SurfaceView.applyTransactionOnVriDraw:2048 android.view.SurfaceView.performSurfaceTransaction:959 android.view.SurfaceView.updateSurface:1105 android.view.SurfaceView.onWindowVisibilityChanged:371 android.view.View.dispatchWindowVisibilityChanged:15943 
    I/SurfaceView@31e8ae4(25559): surfaceDestroyed
    I/SurfaceView@31e8ae4(25559): surfaceDestroyed callback.size 1 #2 io.flutter.embedding.android.FlutterSurfaceView{31e8ae4 V.E...... ........ 0,0-720,1570}
    I/SurfaceView@31e8ae4(25559): updateSurface: mVisible = false mSurface.isValid() = true
    I/SurfaceView@31e8ae4(25559): releaseSurfaces: viewRoot = ViewRootImpl@f9221bd[MainActivity]
    I/SurfaceView@31e8ae4(25559): applyTransactionOnVriDraw: vri = ViewRootImpl@f9221bd[MainActivity] fRS = true t = 0xb40000711bc8e800 android.view.SurfaceView.releaseSurfaces:858 android.view.SurfaceView.updateSurface:1172 android.view.SurfaceView.onWindowVisibilityChanged:371 
    I/SurfaceView@31e8ae4(25559): applyTransactionOnVriDraw: viewRoot.applyTransactionOnDrawFromReleaseSurfaces
    I/ViewRootImpl@f9221bd[MainActivity](25559): from releaseSurfaces t.apply t = 0xb40000711bc8e800
    V/SurfaceView@31e8ae4(25559): Layout: x=0 y=0 w=720 h=1570, frame=Rect(0, 0 - 720, 1570)
    D/SurfaceView@31e8ae4(25559): windowPositionLost, frameNr = 0
    D/OpenGLRenderer(25559): setSurface called with nullptr
    D/OpenGLRenderer(25559): setSurface() destroyed EGLSurface
    D/OpenGLRenderer(25559): destroyEglSurface
    I/ViewRootImpl@f9221bd[MainActivity](25559): performTraversals mFirst=false windowShouldResize=false viewVisibilityChanged=true mForceNextWindowRelayout=false params=null
    I/ViewRootImpl@f9221bd[MainActivity](25559): Relayout returned: old=(0,0,720,1600) new=(0,0,720,1600) req=(720,1600)8 dur=14 res=0x2 s={false 0x0} ch=true seqId=0
    I/SurfaceView@31e8ae4(25559): windowStopped(true) false io.flutter.embedding.android.FlutterSurfaceView{31e8ae4 V.E...... ........ 0,0-720,1570} of ViewRootImpl@f9221bd[MainActivity]
    D/SurfaceView@31e8ae4(25559): updateSurface: surface is not valid
    I/SurfaceView@31e8ae4(25559): releaseSurfaces: viewRoot = ViewRootImpl@f9221bd[MainActivity]
    I/SurfaceView@31e8ae4(25559): applyTransactionOnVriDraw: vri = ViewRootImpl@f9221bd[MainActivity] fRS = true t = 0xb400007106c68600 android.view.SurfaceView.releaseSurfaces:858 android.view.SurfaceView.updateSurface:998 android.view.SurfaceView.setWindowStopped:335 
    I/SurfaceView@31e8ae4(25559): applyTransactionOnVriDraw: viewRoot.applyTransactionOnDrawFromReleaseSurfaces
    I/ViewRootImpl@f9221bd[MainActivity](25559): from releaseSurfaces t.apply t = 0xb400007106c68600
    D/SurfaceView@31e8ae4(25559): updateSurface: surface is not valid
    I/SurfaceView@31e8ae4(25559): releaseSurfaces: viewRoot = ViewRootImpl@f9221bd[MainActivity]
    I/SurfaceView@31e8ae4(25559): applyTransactionOnVriDraw: vri = ViewRootImpl@f9221bd[MainActivity] fRS = true t = 0xb40000707c17bd00 android.view.SurfaceView.releaseSurfaces:858 android.view.SurfaceView.updateSurface:998 android.view.SurfaceView.lambda$new$0$android-view-SurfaceView:200 
    I/SurfaceView@31e8ae4(25559): applyTransactionOnVriDraw: viewRoot.applyTransactionOnDrawFromReleaseSurfaces
    I/ViewRootImpl@f9221bd[MainActivity](25559): from releaseSurfaces t.apply t = 0xb40000707c17bd00
    I/ViewRootImpl@f9221bd[MainActivity](25559): stopped(true) old = false
    I/ViewRootImpl@f9221bd[MainActivity](25559): WindowStopped on com.elektropod.eso/com.example.cso_application.MainActivity set to true
    D/SurfaceView@31e8ae4(25559): updateSurface: surface is not valid
    I/SurfaceView@31e8ae4(25559): releaseSurfaces: viewRoot = ViewRootImpl@f9221bd[MainActivity]
    I/SurfaceView@31e8ae4(25559): applyTransactionOnVriDraw: vri = ViewRootImpl@f9221bd[MainActivity] fRS = true t = 0xb40000707c252200 android.view.SurfaceView.releaseSurfaces:858 android.view.SurfaceView.updateSurface:998 android.view.SurfaceView.setVisibility:392 
    I/SurfaceView@31e8ae4(25559): applyTransactionOnVriDraw: viewRoot.applyTransactionOnDrawFromReleaseSurfaces
    I/ViewRootImpl@f9221bd[MainActivity](25559): from releaseSurfaces t.apply t = 0xb40000707c252200
    I/MSHandlerLifeCycle(25559): removeMultiSplitHandler: no exist. decor=DecorView@b31705[MainActivity]
    D/SurfaceView@31e8ae4(25559): updateSurface: surface is not valid
    I/SurfaceView@31e8ae4(25559): releaseSurfaces: viewRoot = ViewRootImpl@f9221bd[MainActivity]
    I/SurfaceView@31e8ae4(25559): applyTransactionOnVriDraw: vri = ViewRootImpl@f9221bd[MainActivity] fRS = true t = 0xb40000707c252300 android.view.SurfaceView.releaseSurfaces:858 android.view.SurfaceView.updateSurface:998 android.view.SurfaceView.lambda$new$0$android-view-SurfaceView:200 
    I/SurfaceView@31e8ae4(25559): applyTransactionOnVriDraw: viewRoot.applyTransactionOnDrawFromReleaseSurfaces
    I/ViewRootImpl@f9221bd[MainActivity](25559): from releaseSurfaces t.apply t = 0xb40000707c252300
    D/InputTransport(25559): Input channel destroyed: 'ClientS', fd=138

  • AndroidManifest.xml
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />
  • main.dart
Future<void> main() async {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      onInit: () async {
        try {
          var status = await Permission.storage.request();
          if (status.isGranted) {
            print('isGranted');
          } else if (status.isPermanentlyDenied) {
            openAppSettings();
          }
        } catch (e) {
          print('~~error~~~>>>>>> $e');
        }
      },
      theme: ThemeData(
        primaryColor: orangeColor,
      ),
      home: FutureBuilder(
        future: getId(),
        builder: (context, snapshot) {
          if (snapshot.hasData && snapshot.data != null) {
            return const MyHomePage();
          } else {
            return Login();
          }
        },
      ),
      debugShowCheckedModeBanner: false,
    );
  }
}

Solution

  • Based on the Android documentation READ_EXTERNAL_STORAGE permission has no effect starting from API level 33. For API level 33 and above you can use READ_MEDIA_IMAGES, READ_MEDIA_VIDEO, READ_MEDIA_AUDIO permissions depending on your use case

    You can modify your AndroidManifest.xml to ask for relevant permissions depending on the API level:

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" 
    android:maxSdkVersion="32" />
    
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
    <uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/>
    <uses-permission android:name="android.permission.READ_MEDIA_AUDIO"/>