How do i save a generated PDF to Accessible storage? - flutter

I did generated a pdf from HTML code , using html_to_pdf pluggin ,
but it seems that it saves it in DocumentAppPath (data/user/0/com.exmpl.myapp/app_flutter/) which is not something easy to access by a user whom does not know much .
Future<void> generateExampleDocument() async {
var htmlContent = widget.htmlCont; // html Cv File's content
Directory appDocDir = await getApplicationDocumentsDirectory(); // appDocDir
var targetPath = appDocDir.path;
var targetFileName = "example-pdf";
var generatedPdfFile = await FlutterHtmlToPdf.convertFromHtmlContent(
htmlContent, targetPath, targetFileName); // generates pdf document at the given path.
generatedPdfFilePath = generatedPdfFile.path; // variable outside function .
}
then displaying the pdf , it works fine However as i said i want it to be saved somewhere where the user access it for example Download folder .. so after i can simply Open it from that path outside my app .
onPressed: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PDFViewerScaffold(
appBar: AppBar(title: Text("SHOW TEST")),
path: generatedPdfFilePath)),
),
),
Thanks.

I've used esys_flutter_share package. I had same task, but I was generating pdf from the widget using pdf package. So then I've add save button, wich calls share function with options to save the file on mobile or send it someone else.
final sharePdf = await generatedPdfFile.readAsBytes();
await Share.file(
'PDF Document',
'project.pdf',
sharePdf.buffer.asUint8List(),
'*/*',
);

Related

Uploading image into Floating Action Button, set state not working

I have a floating action button that I want a user to click to take a picture with their camera and then have that image replace the camera icon on the floating action bar button.
Here is the code for my FAB, and including uploading the image to firestore storage.
floatingActionButton: FloatingActionButton.large(
heroTag: "add image",
backgroundColor: const Color(0xFF93C3B9),
child: (imageURL == ' ')
? const Icon(Icons.add_a_photo_outlined)
: Image.network(imageURL),
//open add gear page
onPressed: () async {
// todo: upload an image to Firebase Storage
//Take picture
ImagePicker imagePicker = ImagePicker();
XFile? file = await imagePicker.pickImage(source: ImageSource.camera);
if (file == null) return;
String uniqueFileName =
DateTime.now().millisecondsSinceEpoch.toString();
//Get reference to storage root
Reference referenceRoot = FirebaseStorage.instance.ref();
Reference referenceDirImages = referenceRoot.child('images/$userID');
Reference referenceImageToUpload =
referenceDirImages.child(uniqueFileName);
try {
//upload image
await referenceImageToUpload.putFile(File(file.path));
//get download URL
setState(() async {
imageURL = await referenceImageToUpload.getDownloadURL();
print(imageURL);
});
//upload path to fireStore database
} catch (error) {}
},
),
After the image uploads it's like the set state is not working to replace the icon with the image. The odd part is is I crtl-s and save in Visual Studio Code then the widgets seem to rebuild and then the image is visible there...
So after playing around with my code a bit I decided to edit the above code and take tha await function out of the setState() and make setState() not async anymore:
//get download URL
String tempUrl = await referenceImageToUpload.getDownloadURL();
setState(() {
print("--------- Set State -----------");
imageURL = tempUrl;
print("--------- Set State end -----------");
});
print("New image url $imageURL ------------");
not sure why it works, but this solves my issue.
By your description of the issue, I think you might be using StatelessWidget instead of StatefulWidget.
You see the button change when performing a hotreload because the value of imageURL is correctly changing internally, but you need a StatefulWidget to update the UI also.
Hope it helps!

CREATE INVOICE PDF using FLUTTER WEB

I need to create a path to temporarily store my pdf file before sharing it. but path_provider does not support web. please what other way can i use to share the pdf decuments.
knowing that I want to create an incoice.
here is the context with some code
Future getPdf(Uint8List screenShot) async {
pw.Document pdf = pw.Document();
final img = pw.MemoryImage(screenShot);
pdf.addPage(
pw.Page(
pageFormat: PdfPageFormat.a4,
build: (context) {
return pw.Expanded(
child: pw.Image(img),
);
},
),
);
// File pdfFile = File('Your path + File name');
// pdfFile.writeAsBytesSync(await pdf.save());
print(pdf);
}
in this specific case I would like to use an alternative to path_provider

Flutter: save a generated PDF from preview page with a custom function

I'm using the pdf package (link to pub.dev page) to create documents inside my app.
How can I save the document from the preview page?
I know about the built-in print and share functions, but I need something else.
I need a function that does the following:
save the document to the device
upload it to Firebase Storage
delete the local file
I know how to do all of these steps, but I'm unable to access the Uint8List bytes of the pdf document that is being previewed... I've tried to add a PdfPreviewAction, but I still can't access the bytes of the document from it
all you have to do is call the save method in the Document() object
import "package:pdf/widgets.dart" as w;
final pdf = w.Document();
Future<Uint8List> getPDFData() async {
return await pdf.save();
}
you can do all the operations using the getPDFData() method
for example:
return PdfPreview(
build: (format) => getPdfData(),
actions: [
PdfPreviewAction(
icon: Icon(Icons.print),
onPressed: (context, fn, format) {
//if you call fn(format), you'll get Future<UInt8List>
}),
],
);

How do I write to my JSON file more than once? when clicking onPressed?

Each time I restart my code, I get to run my writeToDatabase once, so it adds to list once. But any additional taps on my button does nothing.
When tapping my onPressed button, it sort of "redo" it's function. Or I'm not completely sure. I'm assuming it has something to do with async, but not fully grasping it.
Or maybe it's because I'm using context?
I've tried to set everything to final but it didn't work. Or maybe it's because of "tree shaking"? That it loads the file into memory and that's the file I keep modifying, and not, a new file after each run of my function, when tapping my button?
I got my onPressed:
onPressed: () {
writeToDatabase("wwwwwww", "fff?`!!", context);
},
And I got my writeToDatabase function:
writeToDatabase(dynamic imagePath, dynamic textValue, context) async {
Directory directory = await getApplicationDocumentsDirectory();
String path = directory.path;
File file = File('$path/database.json');
// File file = await _localFile;
String userJson = await DefaultAssetBundle.of(context).loadString(file.path);
List userData = jsonDecode(userJson);
Map<String, dynamic> toJson = {
'imagePath': imagePath,
'textValue': textValue,
};
userData.add(toJson);
return file.writeAsString(json.encode(userData));
}
Thanks for your help, much appreciated!
The answer was right in front of me, I just didn't know where to look.
String userJson = await DefaultAssetBundle.of(context).loadString(file.path);
Has to be changed to:
String userJson = await DefaultAssetBundle.of(context).loadString(file.path, cache: false);
It turns out, it was constantly looking at a cashed version of the file.

type 'Future<File>' is not a subtype of type 'File'

Important: I have searched a lot but did not find any solution that I can understand.
I am using photo manager to show images and then after selecting an image I want to navigate to the next screen and show that image there (later I want to upload that image to firebase storage).
So the type of File I am getting from the photo_manager package is Future < File > and I am receiving File from the second screen.
How can I convert future to file?
First Screen:
IconButton(
icon: const Icon(Icons.arrow_forward, color: primaryColor),
onPressed: () {
Get.to(PostDetail(
imgFile:
_controller.mediaList[_controller.selectedImageIndex].file,
));
},
),
Second Screen:
On the second screen, I'm getting File.
final File imgFile;
const PostDetail({this.imgFile});
Source Code:: Add Post Page
So as described in photo_manager docs, you need to get the file from the AssetEntity list before moving to the next page.
So on button pressed to go to next page get the file and then pass it to the Page
Below code should work:
IconButton(
icon: const Icon(Icons.arrow_forward, color: primaryColor),
onPressed: () async {
File file = await _controller.mediaList[_controller.selectedImageIndex].file; // image file
Get.to(PostDetail(
imgFile: file,
));
},
),
File and Future<File> are 2 different things. the File is a variable that its value is in the present. but Future<File> as its name suggests, its value exist in the Future which means its value takes some time to reach you.
for example, if you send a request to a webserver, the response takes some time to reach you. so, in that case the response is a Future.
in your case the image you are trying to get is supplied in a way that it takes time. to get the File from Future<File> you have to wait. So before sending the file to this Screen, use an async and wait to turn the Future<File> to File. (i dont know how you get the file)
for example,
File myFile = await getFileorWhaterver();
Your _controller.mediaList[_controller.selectedImageIndex].file is a Future<File> means that the value of the File is promised to be returned sometimes in the future. This usually because you are doing a network call asynchronously to get the data from API.
In order to convert it to File, you need to await till the moment that value is available. However you cannot do an async/await within the UI, that's why Flutter provide a widget named FutureBuilder to wait for a certain Future value, then let you use that value in the UI.
In your case, you can do this:
FutureBuilder(
future: _controller.mediaList[_controller.selectedImageIndex].file,
builder: (context, snapshot) {
return IconButton(
icon: const Icon(Icons.arrow_forward, color: primaryColor),
onPressed: () {
if (snapshot.hasData)
Get.to(PostDetail(imgFile: snapshot.data));
},
);
})