Search code examples
androidflutterdartandroid-external-storage

Flutter: android doesn't lets the app create a new folder in the external storage to save file?


I have flutter app which downloads a video and tries to save the video file by particularly "/storage/emulated/0/" directory name video... so the path will be "/storage/emulated/0/video".... but the android doesn't let me create this folder...

My android manifest.xml file consists of external storage permission....

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.infinite_scroll">
   <uses-permission android:name="android.permission.INTERNET"/>
   <!-- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> -->
   <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
   <application
        android:label="video"
        android:icon="@mipmap/ic_launcher"
        android:requestLegacyExternalStorage="true"
        >
        <activity
            android:name=".MainActivity"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">
            <!-- Specifies an Android theme to apply to this Activity as soon as
                 the Android process has started. This theme is visible to the user
                 while the Flutter UI initializes. After that, this theme continues
                 to determine the Window background behind the Flutter UI. -->
            <meta-data
              android:name="io.flutter.embedding.android.NormalTheme"
              android:resource="@style/NormalTheme"
              />
            <!-- Displays an Android View that continues showing the launch screen
                 Drawable until Flutter paints its first frame, then this splash
                 screen fades out. A splash screen is useful to avoid any visual
                 gap between the end of Android's launch screen and the painting of
                 Flutter's first frame. -->
            <meta-data
              android:name="io.flutter.embedding.android.SplashScreenDrawable"
              android:resource="@drawable/launch_background"
              />
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <!-- Don't delete the meta-data below.
             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
        <meta-data
            android:name="flutterEmbedding"
            android:value="2" />
    </application>
</manifest>

I have a downloader function where I pass the file url and filename then it creates the directory and downloads the file in the specific directory

Future<bool> downloadFile(String url, String fileName) async{
    
    try {
      // permission na dile direct failed
      // Android 
        if (Platform.isAndroid){
          if(await _requestPermission(Permission.storage)){
            dir = await getExternalStorageDirectory();
            print(dir!.path);
            // var newPath = dir?.path.split("/data")[0];
            var newPath = dir!.path;
            newPath = newPath + "/video";
            dir = Directory(newPath.toString());
            print(dir?.path);
          }
        }else{
        // IOS
          print("in ios");
          if(await _requestPermission(Permission.photos)){
            dir = await getTemporaryDirectory();
            print(dir);
          }else{
            print("rejected");
            return false;
          }
        }

        if(!await dir!.exists()){
          await dir?.create(recursive: true);  <<<----- this line gives error
        }
        if(await dir!.exists()){
          File saveFile = File(dir!.path + "/$fileName");
          await dio.download(
            url, 
            saveFile.path, 
            onReceiveProgress: (downloaded, totalSize){
              setState(() {
                progress = downloaded / totalSize;
              });
            }
          );
          if(Platform.isIOS){
            await ImageGallerySaver.saveFile(saveFile.path, isReturnPathOfIOS: true);
          }
          return true;
        }
    } catch (e) {
      print(e);
    }
    return false;
  }

the request permission is a function triggered seperately...

  Future<bool> _requestPermission(Permission permission) async{
    if(await permission.isGranted){
      return true;
    }else{
      PermissionStatus result = await permission.request();
      if(result == PermissionStatus.granted){
        return true;
      }else{
        return false;
      }
    }
  }

but when i trigger this function it gives me this error...

I/flutter ( 6252): /storage/emulated/0/Android/data/com.example.infinite_scroll/files
I/flutter ( 6252): /storage/emulated/0/Android/video
I/flutter ( 6252): FileSystemException: Cannot create file, path = '/storage/emulated/0/Android/video/logo1.png' (OS Error: Operation not permitted, errno = 1)
I/flutter ( 6252): failed downloading

I am new to flutter...please provide descriptory answers and suggest me some blogs....


Solution

  • Since Android 11, the way to write on external storage has been changed.

    Read about scoped storage and you may find your answer for the same for Android