sidlatau / flutter_email_sender

Allows send emails from flutter using native platform functionality.
Apache License 2.0
146 stars 81 forks source link

Failed to handle method call : java.lang.IllegalArgumentException: Failed to find configured root that contains /data/data/com.xxxx.yyyyy/app_flutter/Example_English.pdf #66

Closed osamasado closed 2 years ago

osamasado commented 3 years ago

flutter_email_sender: ^4.0.0 path_provider: ^2.0.1

Please help me, how can I solve this issue?

CodingWithTashi commented 2 years ago

I had the same issue, I end up using getTemporaryDirectory from path_provider instead of getApplicationDocumentsDirectory.

LeapingSwan commented 2 years ago

Hi @CodingWithTashi could you please explain in a little more detail how this works for you?

In my case the problem is only on Android. Works fine on iOS. I am creating a PDF in another class and pass the path to that PDF into the class I am working with at this stage. I use that path successfully when creating a PDF review, loading the previously saved file and displaying it fine. However when I use the same path to try and add it as an attachment to an email I get the same error message. If I don't try and add an attachment the email is generated and works fine.

I do currently save the generated file as getApplicationDocumentsDirectory() when creating the file in a previous class. I have tried getTemporaryDirectory() but then the path does not even survive to create the Preview.

The error I get is

Platform Response PlatformException(error, Failed to find configured root that contains /data/data/com.XXXX.XXXX/app_flutter/example.pdf, null, java.lang.IllegalArgumentException: Failed to find configured root that contains /data/data/com.XXXX.XXXX/app_flutter/example.pdf

I have indicated below where the path works and does not work in my full code for the class:

import 'package:XXXX/screens/home/settings_form.dart'; import 'package:XXXX/shared/constants.dart'; import 'package:flutter/material.dart'; import 'package:pdf_viewer_plugin/pdf_viewer_plugin.dart'; import 'package:flutter_email_sender/flutter_email_sender.dart';

class PdfPreviewScreen extends StatefulWidget { final String path;

PdfPreviewScreen({this.path});

@override _PdfPreviewScreenState createState() => _PdfPreviewScreenState(); }

class _PdfPreviewScreenState extends State { String sendLabel = "Attach to Email";

@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( automaticallyImplyLeading: false, title: Text("Your PDF Preview", style: TextStyle(color: Colors.black, fontSize: 12 textAdjust)), ), body: Center( child: Column(children: [ if (widget.path != null) Container( height: 600.0 hAdjust, child: PdfView( path: widget.path, //THIS WORKS FINE ), ) else Text("There was a problem loading the PDF. Please try again"), Padding( padding: const EdgeInsets.all(12.0), child: Stack( children: [ Align( alignment: Alignment.bottomLeft, child: FloatingActionButton.extended( heroTag: "btn1", backgroundColor: barColour, label: Text("Cancel"), onPressed: () { Navigator.pop(context); Navigator.pop(context); }, ), ), Align( alignment: Alignment.bottomRight, child: FloatingActionButton.extended( heroTag: "btn2", backgroundColor: barColour, icon: Icon(Icons.send), label: Text(sendLabel), onPressed: () { generateEmail(context); }, ), ), ], ), ) ]), ), ); }

Future generateEmail(context) async {

if (remainMail < 1) {
  Navigator.push(
      context, MaterialPageRoute(builder: (context) => SettingsForm()));
} else {
  print("Setting up email");
  final Email email = Email(
    body:
        'Please find attached the generated PDF',
    subject: "Generated PDF",
    recipients: [""],
    attachmentPaths: [widget.path],  //THIS WORKS FOR IOS BUT FAILS FOR ANDROID
    isHTML: false,
  );

  String platformResponse;

  try {
    await FlutterEmailSender.send(email);
    platformResponse = 'success';
  } catch (error) {
    platformResponse = error.toString();
    print("Platform Response $platformResponse");
  }

  Navigator.pop(context);
  Navigator.pop(context);

  if (platformResponse == 'success') {
    sendingMail();
  }
}

} }

CodingWithTashi commented 2 years ago

Hi @LeapingSwan, You mentioned you are using getApplicationDocumentsDirectory in your case and I can also see you are passing the path in PdfPreviewScreen. I not able to replicate your issue since code attached is just a snippet of your application. But what I could recommend you is that try getTemporaryDirectory instead of getApplicationDocumentsDirectory.

  1. Here is how I am creating file (.txt) file and sending via email. You might be having existing pdf etc
    
    /// This all field and method are define outside class so that you can use anywhere in your application

late File logFile; // global file it can be txt,pdf,docx etc

Future get _localPath async { final directory = await getTemporaryDirectory(); return directory.path; }

Future get localFile async { final path = await _localPath; logFile = File('$path/logger.txt'); logFile.writeAsString("==================LOG START FROM HERE====================="); return logFile; }

2. Here is how you can get the path and attach to mail   

static void launchEmail(BuildContext context) async { try { final Email email = Email( body: 'Attaching log for ${getFormattedDateFromDateTime( dateTime: DateTime.now(), )}', subject: 'Log', recipients: ['abc@cde.com'], attachmentPaths: [logFile.path], // global file define at first isHTML: false, ); await FlutterEmailSender.send(email); } on PlatformException catch (e) { ApplicationUtil.showSnackBar(context: context, message: e.message!); } catch (e) { print("=========================ERROR========================="); ApplicationUtil.showSnackBar(context: context, message: e.toString()); } }


I have tested in both IOS and android. It worked in both.
LeapingSwan commented 2 years ago

@CodingWithTashi thanks for coming back to me so quickly.

Unfortunately it's a large project so not really practicable to share the full project hence providing the single class. That said the only thing I do elsewhere that is relevant is save the draft PDF when I have created it. For that I use the following:

Future savePdf() async { Directory documentDirectory = await getApplicationDocumentsDirectory();

String documentPath = documentDirectory.path;

File file = File("$documentPath/example.pdf");

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

PdfDoc("$documentPath/example.pdf");

}

and when I go to the class I shared in my original post I then call

Directory documentDirectory = await getApplicationDocumentsDirectory();

String documentPath = documentDirectory.path;

String fullPath = "$documentPath/example.pdf";

Navigator.push(
    context,
    MaterialPageRoute(
        builder: (context) => PdfPreviewScreen(
              path: fullPath,
            )));

I have been trying your suggestion of getTemporaryDirectory but the problem I am having is the file does not seem to persist across classes. So if I use getTemporaryDirectory the file is not available for use when I go to preview the PDF and certainly not to attach it. I am currently exploring trying to re-save the file to the getTemporaryDirectory just before I try and load it as a preview or attachment but can't get that working yet (I'm self taught on Flutter so these things take me a while) but in any case that seems like a bit of a hack even if it does work. Ideally I would save it in the getApplicationsDirectory and have it to use elsewhere just by passing the path.

Any further thoughts welcome.

Thanks

Stephen

LeapingSwan commented 2 years ago

Ok I solved it using, as you suggested the getTemporaryDirectory, and the key to getting that working was putting the file in global location and querying it for it's own path when I needed it rather than trying to pass the path through. That's brilliant, thanks so much for your help.

Stephen

clebemachado commented 2 years ago

Interesting, passing globally works. Worth the tip.

sidlatau commented 2 years ago

Added additional path for PathProvider:

<files-path name="files" path="../"/>

This seems to solve the access problem when the file is in getApplicationDocumentsDirectory().

Fix will be available in v5.1.0.

I am considering adding:

<root-path name="root" path="." />

That also fixes a problem but it is not officially documented, so maybe the first solution will be enough.