How to save File in Downloads folder in flutter? - flutter

In my flutter application I can create a pdf file, then I want to save it in Download folder.
I'm trying to achieve this goal with path_provider package, but I can't.
This is the sample code from flutter's cookbook, If I use it I don't get any error, but I don't find the file either.
final directory = await getApplicationDocumentsDirectory();
File file2 = File("${directory.path}/test.txt");
await file2.writeAsString('TEST ONE');
What's the correct way to do it?

To find the correct path please use ext_storage.
You will need this permission
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
on Android 10 you need this in your manifest
<application
android:requestLegacyExternalStorage="true"
on Android 11 use this instead
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
Remember to ask for them using permission_handler
I leave you my code:
static Future saveInStorage(
String fileName, File file, String extension) async {
await _checkPermission();
String _localPath = (await ExtStorage.getExternalStoragePublicDirectory(
ExtStorage.DIRECTORY_DOWNLOADS))!;
String filePath =
_localPath + "/" + fileName.trim() + "_" + Uuid().v4() + extension;
File fileDef = File(filePath);
await fileDef.create(recursive: true);
Uint8List bytes = await file.readAsBytes();
await fileDef.writeAsBytes(bytes);
}

Android 11 changed a lot of things with its emphasis on scoped storage. Although /storage/emulated/0/Android/data/com.my.app/files is one of the directory paths given by the path_provider pkg, you won't be able to see files saved in /storage/emulated/0/Android/data/com.my.app/files just using any run-of-the-mill file application (Google Files, Samsung My Files, etc.).
A way to get around this (although it only works on Android) is to specify the "general" downloads folder as shown below.
Directory generalDownloadDir = Directory('/storage/emulated/0/Download');
if you write whatever file you are trying to save to that directory, it will show up in the Downloads folder in any standard file manager application, rather than just the application-specific directory that the path_provider pkg provides.
Below is some test code from an app I'm building where I save a user-generated QR code to the user's device. Just for more clarity.
//this code "wraps" the qr widget into an image format
RenderRepaintBoundary boundary = key.currentContext!
.findRenderObject() as RenderRepaintBoundary;
//captures qr image
var image = await boundary.toImage();
String qrName = qrTextController.text;
ByteData? byteData =
await image.toByteData(format: ImageByteFormat.png);
Uint8List pngBytes = byteData!.buffer.asUint8List();
//general downloads folder (accessible by files app) ANDROID ONLY
Directory generalDownloadDir = Directory('/storage/emulated/0/Download'); //! THIS WORKS for android only !!!!!!
//qr image file saved to general downloads folder
File qrJpg = await File('${generalDownloadDir.path}/$qrName.jpg').create();
await qrJpg.writeAsBytes(pngBytes);
Fluttertoast.showToast(msg: ' $qrName QR code was downloaded to ' + generalDownloadDir.path.toString(), gravity: ToastGravity.TOP);

You want getExternalStorageDirectories. You can pass a parameter to specify the downloads specifically:
final directory = (await getExternalStorageDirectories(type: StorageDirectory.downloads)).first!;
File file2 = File("${directory.path}/test.txt");
await file2.writeAsString('TEST ONE');
If you're using null safety you don't need the bang operator:
final directory = (await getExternalStorageDirectories(type: StorageDirectory.downloads)).first;
File file2 = File("${directory.path}/test.txt");
await file2.writeAsString('TEST ONE');

For downloading file in Downloads folder, here are examples:
// Save multiple files
DocumentFileSave.saveMultipleFiles([textBytes, textBytes2], ["text1.txt", "text2.txt"], ["text/plain", "text/plain"]);
//Save single text file
DocumentFileSave.saveFile(textBytes, "my_sample_file.txt", "text/plain");
//Save single pdf file
DocumentFileSave.saveFile(pdfBytes, "my_sample_file.pdf", "appliation/pdf");
//Save single image file
DocumentFileSave.saveFile(imageJPGBytes, "my_sample_file.jpg", "image/jpeg");
More details of library here:
https://pub.dev/packages/document_file_save_plus

Related

How to create a Button that allow user to download a specific file Flutter

I create a flutter app and I have this one CSV file that used as a template for user. I want to provide a Button that allow user to download this CSV file, so they can use it to have CSV file that already have our template.
The problem is I don't know if the best way is to first store the file online and get the url and use it on the flutter downloader URL or keep it in the local code asset and refer to that file when user tap the download template button. Currently I'm applying the second option and it doesn't work (I don't know if this option is possible or not), the download always fail. I'm using flutter_downloader package.
How to fix this ?
Here's my code, Is something wrong with my code ?
/// Check if the file exist or not
if (await File(externalDir!.path + "/" + fileName).exists()) {
OpenFilex.open(externalDir!.path + "/" + fileName);
} else {
/// Download the file if it doesn't exist in the user's device
final String localPath = (await getApplicationDocumentsDirectory()).path;
/// Dummy file name I want use (it exist in my asset dir"
const String fileName = 'add.png';
final data = await rootBundle.load('assets/logo/add.png');
final bytes = data.buffer.asUint8List();
final File file = File('$localPath/$fileName');
await file.writeAsBytes(bytes);
/// Download the file
final taskId = await FlutterDownloader.enqueue(
url: '',
savedDir: localPath,
fileName: fileName,
showNotification: true,
openFileFromNotification: true,
);
}
To load a file from the AppBundle and then save it to the users phone, do the following:
Put the file in assets/filename.csv and declare it in your pubspec like this:
flutter:
assets:
- assets/filename.csv
Load the file in your code:
import 'package:flutter/services.dart' show ByteData, rootBundle;
(...)
var data = (await rootBundle.load('assets/filename.csv)).buffer.asInt8List();
Save the data to a file (you need the path-provider package if you want to copy the exact code):
import 'package:path_provider/path_provider.dart' as pp;
(...)
var path = (await pp.getApplicationDocumentsDirectory()).path;
var file = File('$path/filename.csv');
await file.writeAsBytes(data, flush: true);
Edit: As Stephan correctly pointed out, if you want to store the file in the downloads folder, you will find additional information about that here. Thank you Stephan!

Flutter/Dart - File.writeAsBytes executes with no error but can't find the file

I have a base64 encoded string of a PDF file. I'm trying to save it as a file in the android device. My code runs with no error, but i can't find the file.
Uint8List bytes = base64.decode(encodedStr);
String dir = (await getExternalStorageDirectories(
type: StorageDirectory.downloads))!
.first
.path;
File file = File("$dir/" +
DateTime.now().millisecondsSinceEpoch.toString() +
".pdf");
await file.writeAsBytes(bytes);
Also, I tried to include code to ask for permission before trying to save the file, but it didn't change anything.
var status = await Permission.storage.status;
if (!status.isGranted) {
await Permission.storage.request();
}
if (await Permission.storage.request().isGranted) {
try {
await file.writeAsBytes(bytes);
} catch (e) {
print(e);
}
}
And, as another attempt, I included these permission on my AndroidManifest file. Also, no changes.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION"/>
<uses-permission android:name="android.permission.READ_INTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
//dir = /storage/emulated/0/Android/data/com.xxxxxxxxxxxxxxxx.xxxxx/files/Download
//dir + file = /storage/emulated/0/Android/data/com.xxxxxxxxxxxxxxxx.xxxxx/files/Download/1655924904315.pdf
//file path after written = /storage/emulated/0/Android/data/com.xxxxxxxxxxxxxxxx.xxxxx/files/Download/1655924904315.pdf
Is there anything wrong with my code or something that I can change in order for it to work ?
The above code is using path_provider and permission_handler packages and was tested in an emulator and in a physical device.
Thanks.
Try importing io and use file of io and use this package https://pub.dev/packages/pdf
import 'dart:io' as io;
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
final pdf = pw.Document();
final dir = await getTemporaryDirectory();
final path = "${dir.path}/pdfname.pdf";
final file = await io.File(path).writeAsBytes(pdf.save());
It happens that the files are being correctly generated, but they are being saved in the application's directory. A download folder has being created and the files are being saved there. I didn't find the files searching via android, but when I connected my physical device to a PC I was able the find them.
It seems that this has something to do with this https://github.com/flutter/flutter/issues/35783 . Path_provider started to return application document directory after 1.1.0 instead of the general downloads folder.
I'll check how to access the path I want correctly.

flutter ImageGallerySaver ios gif download error

I'm using ImageGallerySaver for downloading gif files.
At Android platform, It takes good.
But iOS platform, gif files save as jpg. So, files not animated...
please help me.
My code likes this.
var appDocDir = await getTemporaryDirectory();
String savePath = appDocDir.path + "/temp.gif";
await Dio().download(imgUrl, savePath);
final result = await ImageGallerySaver.saveFile(savePath);

In file picker in flutter, path was not unique

I'm trying with two different images with same name. But the path was same for two picked images. it was not unique.So that I uploaded in server second image with the same name of first uploaded image. But server had both the image are same and it had first image. So how to handle this case and customize the path?
You can use the path_provider to define the customize directory on your app.
So, copy the file with your customize path and rename the file name.
BTW, do NOT save the absolute path of File on iOS.
The iOS use SandBox to access the file. When you get the file path every time. The file path will be different.
class FileUtils {
final String avatarPath = '/avatar/';
Future<String> getAvatarDirectoryPath() async {
final String appDirPath = await getApplicationSupportDirectory().path;
final Directory avatarDirPath = await Directory(appDirPath + avatarPath).create();
return directory.path;
}
}
// Example
{
final XFile? image = await ImagePicker().pickImage(source: ImageSource.camera);
final File imageFile = File(image.path);
final File newFile = File(await FileUtils().getAvatarDirectoryPath() + 'userAvatar.png');
await imageFile.copy(newFile.path);
}

Load .pdf file / custom file from flutter assets

My question is if there is a way to load a .pdf file in app resources like assets when deploying flutter app ? I'd like to display a PDF file using this:
https://pub.dartlang.org/packages/flutter_full_pdf_viewer
but without loading from internet ;)
Copy the asset to a temporary file.
Future<File> copyAsset() async {
Directory tempDir = await getTemporaryDirectory();
String tempPath = tempDir.path;
File tempFile = File('$tempPath/copy.pdf');
ByteData bd = await rootBundle.load('assets/asset.pdf');
await tempFile.writeAsBytes(bd.buffer.asUint8List(), flush: true);
return tempFile;
}