Abhishek Tiwari
Abhishek Tiwari

Reputation: 55

Unhandled Exception: PathAccessException: Cannot open file, path even when permission were given in flutter

I am working on a flutter project where I need to create a Digital Id for user and save a pdf of it. So for that I used "PDF" package. Currently I am just creating a PDF with text "Dart is awesome". But I am getting an error when I tap the Download Id button and select the folder location where I want to save my pdf. The error is :

E/flutter (31521): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: PathAccessException: Cannot open file, path = '/storage/emulated/0/1Cd/example.pdf' (OS Error: Permission denied, errno = 13) E/flutter (31521): #0 _checkForErrorResponse (dart:io/common.dart:55:9) E/flutter (31521): #1 _File.open. (dart:io/file_impl.dart:381:7) E/flutter (31521): E/flutter (31521): #2 DigitalId.build. (package:rightclaim/screens/digital_id/digital_id.dart:241:9) E/flutter (31521): E/flutter (31521):

I have given all permissions: main/AndroidManifest

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.rightclaim">
   <application
        android:label="rightclaim"
        android:name="${applicationName}"
        android:icon="@mipmap/ic_launcher">
        <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">
            <!-- 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" />
    </application>
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

</manifest>

debug/AndroidManifest

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.rightclaim">
    <!-- The INTERNET permission is required for development. Specifically,
         the Flutter tool needs it to communicate with the running application
         to allow setting breakpoints, to provide hot reload, etc.
    -->
       <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.INTERNET"/>
</manifest>

profile/AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.rightclaim">
    <!-- The INTERNET permission is required for development. Specifically,
         the Flutter tool needs it to communicate with the running application
         to allow setting breakpoints, to provide hot reload, etc.
    -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.INTERNET"/>
</manifest>

Here is my code for digital_id.dart You can skip to CommonElevatedButton.buttonWithoutIcon(...) where I am doing the pdf work.

import 'dart:io';

import "package:file_picker/file_picker.dart";
import "package:flutter/material.dart";
import "package:flutter/services.dart";
import "package:flutter_svg/svg.dart";
import 'package:path/path.dart' as path;
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import "package:permission_handler/permission_handler.dart";
import "package:right_claim_common/color_constants.dart";
import "package:right_claim_common/common_appbar.dart";
import "package:right_claim_common/common_elevated_buttons.dart";
import "package:right_claim_common/common_textsyles.dart";

import "../../user_data/user_data.dart";
import "userid_details.dart";

class DigitalId extends StatelessWidget {
  UserDigitalId userId = UserDigitalId(
      fullName.toString(),
      "XXXX XXXX XXXX XXXX",
      "12455",
      "9568743367",
      "[email protected]",
      "10/10/2022",
      "Raj Nagar Colony Shahdara Agar,Shahdara",
      "6152 7298 8282",
      "AB+");

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: CommonAppBar(title: "Digital Id"),
        body: Align(
          alignment: Alignment.topCenter,
          child: Padding(
              padding: const EdgeInsets.all(20.0),
              child:
                  Column(mainAxisAlignment: MainAxisAlignment.start, children: [
                Container(
                  width: double.infinity,
                  decoration: BoxDecoration(
                    borderRadius:
                        const BorderRadius.vertical(top: Radius.circular(10)),
                    color: ColorConstants.primaryColor,
                  ),
                  child: Column(
                    children: [
                      Column(
                        children: [
                          Container(
                              padding: EdgeInsets.all(10),
                              decoration: BoxDecoration(
                                borderRadius: BorderRadius.vertical(
                                    top: Radius.circular(10)),
                                color: ColorConstants.tertiaryColor,
                              ),
                            
                              child: Row(
                                children: [
                                  Text(
                                    "Right Claim",
                                    style: CommonTextStyles.text20_700()!
                                        .copyWith(
                                            color: ColorConstants.primaryColor),
                                  ),
                                  Spacer(),
                                  ClipOval(
                                    child: SvgPicture.asset(
                                      'assets/logo2.svg',
                                      width: 50,
                                      height: 50,
                                    ),
                                  ),
                                ],
                              )),
                          Container(
                            padding: EdgeInsets.all(16.0),
                            child: Row(
                              crossAxisAlignment: CrossAxisAlignment.start,
                              children: [
                                Expanded(
                                  child: Column(
                                    crossAxisAlignment:
                                        CrossAxisAlignment.start,
                                    children: [
                                      Text(
                                        '${userId.name}',
                                        style: CommonTextStyles.text20_800()!
                                            .copyWith(color: Colors.black),
                                      ),
                                      SizedBox(
                                        height: 20,
                                      ),
                                      Text(
                                        'VHDM Number: ${userId.vhdmNumber}',
                                        style: CommonTextStyles.text14_700()!
                                            .copyWith(color: Colors.black),
                                      ),
                                      SizedBox(
                                        height: 10,
                                      ),
                                      Text(
                                        'Member ID: ${userId.memberId}',
                                        style: CommonTextStyles.text14_700()!
                                            .copyWith(color: Colors.black),
                                      ),
                                      SizedBox(
                                        height: 10,
                                      ),
                                      Text(
                                        'Phone Number: ${userId.phoneNumber}',
                                        style: CommonTextStyles.text14_700()!
                                            .copyWith(color: Colors.black),
                                      ),
                                      SizedBox(
                                        height: 10,
                                      ),
                                      Text(
                                        'Email: ${userId.email}',
                                        style: CommonTextStyles.text14_700()!
                                            .copyWith(color: Colors.black),
                                      ),
                                      SizedBox(
                                        height: 10,
                                      ),
                                      Text(
                                        'DOB: ${userId.dob}',
                                        style: CommonTextStyles.text14_700()!
                                            .copyWith(color: Colors.black),
                                      ),
                                      SizedBox(
                                        height: 10,
                                      ),
                                      Text(
                                        'Address: ${userId.address}',
                                        style: CommonTextStyles.text14_700()!
                                            .copyWith(color: Colors.black),
                                      ),
                                      SizedBox(
                                        height: 10,
                                      ),
                                      Text(
                                        'Adhar Card: ${userId.adharCard}',
                                        style: CommonTextStyles.text14_700()!
                                            .copyWith(color: Colors.black),
                                      ),
                                      SizedBox(
                                        height: 10,
                                      ),
                                      Text(
                                        'Blood Group: ${userId.bloodGroup}',
                                        style: CommonTextStyles.text14_700()!
                                            .copyWith(color: Colors.black),
                                      ),
                                      SizedBox(
                                        height: 20,
                                      ),
                                      // Add more user data here
                                    ],
                                  ),
                                ),
                                SizedBox(
                                    width:
                                        16.0), // Add some spacing between the text and image
                                Stack(
                                    alignment: Alignment.bottomCenter,
                                    children: [
                                      Container(
                                        width: 77,
                                        height: 100,
                                        decoration: BoxDecoration(
                                          // color: Colors.red,

                                          image: DecorationImage(
                                            image:
                                                AssetImage('assets/logo.png'),
                                            fit: BoxFit.cover,
                                          ),
                                        ),
                                      ),
                                      Container(
                                        width: 89,
                                        height: 20,
                                        color: ColorConstants.quaternaryColor,
                                        alignment: Alignment.center,
                                        child: Text(
                                          "Member",
                                          style: TextStyle(color: Colors.black),
                                        ),
                                      )
                                    ]),
                              ],
                            ),
                          ),
                        ],
                      ),
                    ],
                  ),
                ),
                SizedBox(
                  height: 40,
                ),
                CommonElevatedButton.buttonWithoutIcon(
                  title: "Download Id",
                  //the function where i am trying to create a pdf and save it.
                  onButtonPressed: () async {
                    final pdf = pw.Document();
                    final font =
                        await rootBundle.load("assets/Roboto/Roboto-Black.ttf");
                    final ttf = pw.Font.ttf(font);
                    pdf.addPage(pw.Page(
                      pageFormat: PdfPageFormat.a4,
                      build: (pw.Context context) {
                        return pw.Center(
                          child: pw.Text('Dart is awesome',
                              style: pw.TextStyle(font: ttf, fontSize: 40)),
                        );
                      },
                    ));

                    // Request necessary permissions
                    final status = await Permission.storage.request();
                    if (status.isGranted) {
                      // Get the directory path
                      final directoryPath =
                          await FilePicker.platform.getDirectoryPath();
                      if (directoryPath != null) {
                        final directory = Directory(directoryPath);
                        final fileName = 'example.pdf';
                        final filePath = path.join(directory.path, fileName);
                        final file = File(filePath);

                        await file.writeAsBytes(await pdf.save());

                        print('PDF saved to $filePath');
                      } else {
                        print('No folder selected');
                      }
                    } else {
                      print('Permission denied');
                    }
                  },
                )
              ])),
        ));
  }
}

Upvotes: 2

Views: 5623

Answers (2)

Elias Dalvite
Elias Dalvite

Reputation: 31

I resolved the issue by changing the android:requestLegacyExternalStorage field within the <application> tag in the AndroidManifest from false to true. After recompiling from scratch, it worked. If it doesn't, try uninstalling the app and compiling again.

It's important to note that I have two test devices, one running Android 10 and the other Android 11. The Android 11 device works regardless of the value of android:requestLegacyExternalStorage. However, the Android 10 device consistently encountered errors, which I resolved using the method mentioned above.

Upvotes: 2

mc100p
mc100p

Reputation: 301

//Not sure if you have to save it in that file directory. Only got that issue when I didn't have .pdf at the end of the filename I was trying to store so it wouldn't know what kind of document its actually working with.

class DownloadPDF extends StatefulWidget {
  const DownloadPDF({super.key
    });

  @override
  State<DownloadPDF> createState() => _DownloadPDFState();
}

class _DownloadPDFState extends State<DownloadPDF> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
          onPressed: () => createPDF(), child: const Icon(Icons.download)),
    );
    }

  Future<Uint8List?> createPDF() async {
    final pdf = pw.Document();
    List headers = [
            "Property",
            "Value",
        ];
    List values = [CustomRow(name: "testing", value: "value")
        ];
    final data = values.map((e) => [e.name, e.value
        ]).toList();
    pdf.addPage(pw.MultiPage(
        build: (context) => [
              pw.Column(children: [
                pw.SizedBox(height: 100),
                pw.Text(
                    'Dart is awesome',
                    style: pw.TextStyle(fontWeight: pw.FontWeight.bold)),
            ]),
              pw.SizedBox(height: 100),
              pw.TableHelper.fromTextArray(
                headers: headers,
                data: data,
                tableWidth: pw.TableWidth.max,
                border: pw.TableBorder.all(
                  color: PdfColors.black,
                  width: 1.0,
                ),
              ),
        ]));
    List<int> bytes = await pdf.save();
    saveAndLaunchFile(bytes, 'PDF Document.pdf');
    return pdf.save();
    }

  Future<void> saveAndLaunchFile(List<int> bytes, String fileName) async {
    bool dirDownloadExists = true;
    String androidDirectory;
    androidDirectory = "/storage/emulated/0/Download/";

    dirDownloadExists = await Directory(androidDirectory).exists();

    if (dirDownloadExists) {
      androidDirectory = "/storage/emulated/0/Download";
        } else {
      androidDirectory = "/storage/emulated/0/Downloads";
        }
    final path = Platform.isAndroid
        ? androidDirectory
        : (await getApplicationDocumentsDirectory()).path;
    final file = File('$path/$fileName');
    await file.writeAsBytes(bytes, flush: true);
    OpenFile.open('$path/$fileName');
    }
}

class CustomRow {
  final String name;
  final String value;

  CustomRow({required this.name, required this.value
    });
}

//IF YOU WANT THE FILE TO OPEN INSTANTLY AFTER DOWNLOADING YOU COULD USE OPENFILE PLUS BUT SOME THINGS WILL BE NEEDED TO BE ADDED TO YOUR ANDROID MANIFEST

   <manifest xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:tools="http://schemas.android.com/tools" package="xxx.xxx.xxxxx">
<application>
    ...
    <provider android:name="androidx.core.content.FileProvider"
              android:authorities="${applicationId}.fileProvider"
              android:exported="false"
              android:grantUriPermissions="true"
              tools:replace="android:authorities">
        <meta-data android:name="android.support.FILE_PROVIDER_PATHS"
                   android:resource="@xml/filepaths"
                   tools:replace="android:resource" />
    </provider>
</application>

ENSURE TO REPLACE xxx.xxx.xxxxx WITH UR PACKAGE NAME

This works for both iOS and android

hope this helps...

enter image description here

Upvotes: 0

Related Questions