Search code examples
androidflutterscreenshotandroid-12flutter-plugin

Flutter native_screenshot on Android 12 throws path as null and failed EPERM (Operation not permitted) even with AndroidManifest.xml R/W/M permissions


Building: An app that runs in the background and takes screenshots periodically

Screnario: For now i'm just trying to take the screenshot with a press of a button. App doesn't have to run in the background

Clarification: Not in app screenshot but native screenshots (Android only)

Problem: Throws path as null and doesn't save screenshot

Versions:

  • Flutter: 3.7.8
  • permission_handler: ^10.2.0
  • native_screenshot: ^1.0.0
  • minSdkVersion 23

Error log:

I/NativeScreenshotPlugin(15682): Permission to write granted!
I/NativeScreenshotPlugin(15682): Trying to take screenshot [old way]
I/NativeScreenshotPlugin(15682): Built ScreeshotPath: /storage/emulated/0/native_screenshot-20230328162022.png
I/NativeScreenshotPlugin(15682): Error writing bitmap: /storage/emulated/0/native_screenshot-20230328162022.png: open failed: EPERM (Operation not permitted)
I/NativeScreenshotPlugin(15682): The bitmap cannot be written, invalid path.
I/flutter (15682): Screenshot taken, path: null

main\AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.example.reelback">
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />

   <application
        android:label="reelback"
        android:name="${applicationName}"
        android:icon="@mipmap/ic_launcher"
        android:requestLegacyExternalStorage="true"
        android:preserveLegacyExternalStorage="true">
        <activity
            android:name=".MainActivity"
            android:exported="true"
            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"
            tools:ignore="Instantiatable">
            <!-- 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"
              />
            <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" />

       <service android:name="flutter.overlay.window.flutter_overlay_window.OverlayService" android:exported="false"
           tools:ignore="MissingClass" />
    </application>
</manifest>

Plugins:

  • import 'package:permission_handler/permission_handler.dart';
  • import 'package:native_screenshot/native_screenshot.dart';

main.dart implementation bit code:

                  FilledButton(
                    onPressed: () async {
                      await [Permission.storage].request();

                      String? path = await NativeScreenshot.takeScreenshot();

                      debugPrint('Screenshot taken, path: $path');

                      if( path == null || path.isEmpty ) {
                        ScaffoldMessenger.of(context).showSnackBar(
                            SnackBar(
                              content: Text('Error taking the screenshot :('),
                              backgroundColor: Colors.red,
                            )
                        );

                        return;
                      }

                      ScaffoldMessenger.of(context).showSnackBar(
                          SnackBar(
                              content: Text('The screenshot has been saved to: $path')
                          )
                      );

                    },
                    child: Text('Screenshot Test'),
                  ),

I tried with native_screenshot_ext plugin but the result is same.


Solution

  • I got the same problem, they dont have enough documentation of native_screenshot.

    I created a solution maybe this help you, because mine is now working.

    since you have have "permission_handler" on you project just follow this steps on your main function:

    void main() async {
    
        WidgetsFlutterBinding.ensureInitialized();
        
        if (await Permission.manageExternalStorage.isDenied) {
    
        debugPrint('no access storage');
        await Permission.manageExternalStorage.request();
        debugPrint('access storage granted');
    
        }
        
        runApp(const MyApp());
    
    }