Search code examples
androidflutterdartandroid-permissionsfile-permissions

Create directory denied on Samsung phone even after Storage Permissions are granted


At the launch of my app, I create a folder in storage. To achieve that I ask for permissions within initState(). Only when it completes should the directory check go forward.

@override
  void initState(){
    super.initState();
    easyDir = Directory(widget.folderPath);
    getPermissionStatus().whenComplete(() {
      setState(() {
        //never call async operations in setState, do it in a function call.
        directoryCheck();
      });
    });
  }

the folderPath is produced by the permission_handler package. Which for my phone is /storage/emulated/0/Documents/easyFolder

  //check if directory exists, create if not.
  Future<void> directoryCheck() async {
    // using awaits fixed my problem of setstate being called before
    // directory was created.. and calling setState at end
    exists = await easyDir.exists();
    if (!exists){
      await new Directory(widget.folderPath).create(recursive: true);
      exists = true;
    }
    setState(() {

    });

  }

Future<void> getPermissionStatus() async {
    _status = await Permission.storage.status;
    // if permission is not granted yet, request it and update _status
    if (_status != PermissionStatus.granted){
      await Permission.storage.request();
      _status = await Permission.storage.status;
    }
  }

This works as expected on the emulator. The app asks for permission at startup, waits for it to complete, and when allowed creates the directory.

But when installed on my Samsung phone and even a friends phone (which is Samsung) the folder cannot be created due to permission denied, even after granting permission.

This is the error I receive on my Samsung phone:

E/flutter (23275): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: FileSystemException: Creation failed, path = '/storage/emulated/0/Documents' (OS Error: Permission denied, errno = 13)

I clearly gave permission at startup of app. Whats more is when I go into the appinfo of my app it shows that Storage permissions are granted.

I tried installing this on Huawei and Xiaomi phones and both worked as expected.

What am I doing wrong here?

Here are the permission lines in my AndroindManifest

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET"/>

Here is the error stack if that helps:

E/flutter (23275): #0      _Directory.create.<anonymous closure> (dart:io/directory_impl.dart:124:11)
E/flutter (23275): #1      _rootRunUnary (dart:async/zone.dart:1192:38)
E/flutter (23275): #2      _CustomZone.runUnary (dart:async/zone.dart:1085:19)
E/flutter (23275): #3      _FutureListener.handleValue (dart:async/future_impl.dart:141:18)
E/flutter (23275): #4      Future._propagateToListeners.handleValueCallback (dart:async/future_impl.dart:682:45)
E/flutter (23275): #5      Future._propagateToListeners (dart:async/future_impl.dart:711:32)
E/flutter (23275): #6      Future._completeWithValue (dart:async/future_impl.dart:526:5)
E/flutter (23275): #7      Future._asyncComplete.<anonymous closure> (dart:async/future_impl.dart:556:7)
E/flutter (23275): #8      _rootRun (dart:async/zone.dart:1184:13)
E/flutter (23275): #9      _CustomZone.run (dart:async/zone.dart:1077:19)
E/flutter (23275): #10     _CustomZone.runGuarded (dart:async/zone.dart:979:7)
E/flutter (23275): #11     _CustomZone.bindCallbackGuarded.<anonymous closure> (dart:async/zone.dart:1019:23)
E/flutter (23275): #12     _microtaskLoop (dart:async/schedule_microtask.dart:43:21)
E/flutter (23275): #13     _startMicrotaskLoop (dart:async/schedule_microtask.dart:52:5)
E/flutter (23275): 

Solution

  • I find weird that the code works fine in Android 11 but not 10, because of the new scoped storage the writting permissions should fails in Android 10 (Q) and above

    For now try this in your manifest to opt out of scoped storage in Android 10 (this will do nothing in Android 11),

    <manifest ... >
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.INTERNET"/>
    <!-- This attribute is "false" by default on apps targeting
         Android 10 or higher. -->
      <application android:requestLegacyExternalStorage="true" ... >
        ...
      </application>
    </manifest>
    

    Caution: After you update your app to target Android 11 (API level 30), the system ignores the requestLegacyExternalStorage attribute when your app is running on Android 11 devices, so your app must be ready to support scoped storage and to migrate app data for users on those devices.

    More info about Scoped Storage here

    Now some questions to help you fix this problem:

    the folderPath is produced by the permission_handler

    I don't recall permission_handler to return a path when asking for permissions, can you elaborate how do you get widget.folderPath?

    The Emulator is running Android version 11. While my Samsung phone is on Android version 10

    How about the Huawei and Xiaomi devices, are they running Android 10 too?

    What is your compileSdkVersion/targetSdkVersion in gradle or targetSdk in AndroidManifest?