How to Stream local file modifications in flutter - flutter

I was looking for a way where I could listen to changes performed to a file. For example, there is a file say 'x.json' where I will read and write on this file.
I will write data like, write['key']['c'] = 3 //just for representation purposes.
{
"key":{
"a":1,
"b":2,
"c":3
}
}
since 'c':3 has been added to 'key', I want that to reflect in my app. ie: read
My Code:
final directory = await getApplicationDocumentsDirectory();
File xfile = File('${directory.path}/x.json');
_fileSubscription = xfile.watch().listen((FileSystemEvent event) {
if (mounted) {
setState(() {
currentXFileContent= jsonDecode(xfile.readAsStringSync());
print('Within stream, currentXFileContent: $currentXFileContent');
});
}
});
I get this message
Unhandled Exception: FileSystemException: File system watching is not supported on this platform, path = ''
How can I solve this?

Related

Writing to file in Flutter multiple times updates the file. Reading from the file always gives me the initial content

SOLVED FOR MY SITUATION. MORE INFORMATION HERE AND THEN ORIGINAL QUESTION BELOW.
===Solution===
Due to settings with Android external storage, the file_picker plugin creates a cache of the file you pick and stores it in a cache directory within the app storage location. It will not overwrite this for files with the same name on subsequent reads. So for my read/write app, the solution was to do await file.delete(); when I was done with the read operation. This ensures that the next read will then create a cached version with the updated contents
===Original Question===
I have some content in a database on a Flutter app I am using to just practice some new stuff in FLutter. I have an export button that gets this data, JSON encodes it, and writes it to a file.
If I change the content and then export a second time, I can open the file on my device and see the updated content. I also have an import button. When I press that, I use FilePicker to select a file, read the contents of the file, and then JSON decode the data into an object.
I print out the file.readAsString and see the content from the initial write.
If I manually delete the file between writes then it works. If I use file.delete() before the write, it does not work. What can I do to get the updated text when I read from the file?
Getting file to write to. (I am aware this will only work on Android as is and that's fine)
Future<File?> _getBackupDataFile(String pathToTryFirst, ExportData data) async {
Directory? directory = Directory(pathToTryFirst);
if (!await directory.exists()) directory = await getExternalStorageDirectory();
if ((await directory?.exists() ?? false) == false) {
showErrorDialog(context: context, body: "Unable to find directory to save file.");
return null;
}
return File("${directory?.path}/pm-account-backup.json");
}
Write to file as such (without the delete code):
Future<void> _writeDataToFile(ExportData data) async {
try {
File? file = await _getBackupDataFile('/storage/emulated/0/Download', data);
if(file == null) { return; }
await file.writeAsString(jsonEncode(data));
await showSuccessDialog(context: context, title: "Success", body: "${data.accounts.length} accounts backed up successfully.");
} catch (e) {
showErrorDialog(context: context, body: "Failed to write data to file.");
}
}
Simplified file pick:
FilePickerResult? result = await FilePicker.platform.pickFiles();
if (result != null) {
String path = result.files.single.path ?? '';
if((path).endsWith(".json")) {
return File(path);
}
}
Read from file as such:
String fileData = await file.readAsString();
print(fileData);
Solution for my question found after more information provided by #pskink
Due to settings with Android external storage, the file_picker plugin creates a cache of the file you pick and stores it in a cache directory within the app storage location. It will not overwrite this for files with the same name on subsequent reads. So for my read/write app, the solution was to do await file.delete(); when I was done with the read operation. This ensures that the next read will then create a cached version with the updated contents

File stored in app document directory always exists although it hasn't been created (Flutter)

I am currently developing a mobile app with flutter and I want to display the profile picture of a user and their friends.
The pictures are stored in Firebase Storage, but to minimize the number of requests I want to load each image once and then store it locally.
I wrote a function to get the image from Firebase storage and store it in the app document directory, but I can't figure out how to execute the function only when the file doesn't already exist locally.
This is an excerpt of my code
Future<File?> getProfilePic(String? uid) async {
Directory appDocumentsDirectory = await getApplicationDocumentsDirectory();
String filepath = "${appDocumentsDirectory.path}/profiles/$uid.jpg";
if (await File(filepath).exists()) {
return File(filepath);
} else {
var success = await downloadFromFirebaseAndSaveToLocal(
"profilepic/$uid.jpg", filepath);
if (success) {
getProfilePic(uid);
}
}
return null;
}
Somehow the condition of the if statement (await File(filepath).exists()) is always true. I guess it makes sense. I think the file exists in the directory but doesn't have any content.
Does anyone know how to check if the file has content?
A normal null check doesn't work.
Thank you for helping!
To check the size of the file, you can
if (File(filepath).lengthSync() != 0)
or
You can await the file.length()

How to run Amplify's uploadFile function within a dart isolate?

My application has a function that allows to upload videos and other large files to Amazon S3 buckets via Amplify storage. The below function works as expected and uploads a file to my S3 bucket:
Future<String> uploadFile({File? file, String path = "", String fileName = "", String fileExtension = ""}) async {
try {
final key = path+fileName+'.'+fileExtension;
final result = await Amplify.Storage.uploadFile(
local: file!,
key: key,
);
return result.key;
} catch (e) {
throw e;
} }
However, given the size of some of these files, the time required to upload can be large and I want to avoid that my users have to wait until the file is completely uploaded. Therefore I want to run the above function in a dart Isolate, so that the file can continue uploading while my user Navigates to different screens of the app.
I tried to achieve this with the below function:
static uploadFileIsolate(List<dynamic> args) async {
try {
SendPort responsePort = args[0];
File file = args[1];
String path = args[2];
String fileName = args[3];
String fileExtension = args[4];
final key = path+fileName+'.'+fileExtension;
final result = await Amplify.Storage.uploadFile(
local: file,
key: key,
);
Isolate.exit(responsePort, result);
} catch (e) {
throw e;
} }
Which I call like this from my main function:
final p = ReceivePort();
Isolate.spawn(uploadFileIsolate, [p.sendPort, file, path, fileName, fileExtension]);
This is not working and throws the below error:
[VERBOSE-2:dart_isolate.cc(1111)] Unhandled exception:
RangeError (index): Invalid value: Valid value range is empty: 0
#0 DatabaseService.uploadFileIsolate (package:mastory/services/database_service.dart:202:7)
#1 _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:300:17)
#2 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:192:12)
After reading this issue it makes me think that the error comes from the fact that Amplify is not configured in the 'isolated' environment.
So my question is: How can I make Amplify's uploadFile function work in an Isolate?
Or, if that is not possible, how can I make sure that the uploadFile function continues until completion while allowing my user to Navigate to other screens in my app?

FileSystemException: Cannot open file, path = 'storage/emulated/0/DCIM/docs/myPdf.pdf' (OS Error: Operation not permitted, errno = 1)

I am trying to create a pdf document and write my data in that file. The data is a list of int or uint type. Able to do the same for images but not pdf or doc file. All permissions are given and it works for images. My code is below -
Future <File> createDocFile(Uint8List fileData, String type) async
{
File file = new File(".pdf");
if(await PermissionHandler.checkPermission(Permission.storage)!=true){
Url.toastShow("Storage permission not found.");
await Permission.storage.request();
}
else {
await file.writeAsBytes(fileData);
print("PDF SAVED IN DEVICE");
Url.toastShow("PDF saved in device",Colors.green);
}
}
Whenever you create a new file the device can't automatically find it. You'd have to manually tell the device to refresh for the files. Before you'd have to refresh all the files in the device for it to get updated which was very inefficient, but now you could just send the path of the file that you want to get updated. You can use the media_scanner plugin to do so.
Or If you wanna do it yourself with kotlin then here's the code,
private fun broadcastFileUpdate(path: String){
context.sendBroadcast(Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,Uri.fromFile(File(path))))
}

Flutter create a file and write its contents

I am working on an app which gathers text content from server and as the next step I am trying to save them into separate files to be saved with in apps storage. Storage is a folder called 'storage'. Inside storage, there is a 'fileList.txt' file which contains the list of files.
In pubspec.yaml I declared this folder in the assets as follows:
assets:
- storage/
And I can read it using the following function:
Future<String> readFileContent() async {
String response;
response =
await rootBundle.loadString('storage/fileList.txt');
return response;
}
To save the file I made use of online examples which use 'path_provider' package. However when I try to save one of the downloaded files for instance 'file4.dat' using the following functions, I encounter the following error. How can I resolve that problem?
saveFileLocally() async {
await _writeFile();
}
Future<Null> _writeFile() async {
await (await _getLocalFile()).writeAsString(vars.fileContents);
}
Future<File> _getLocalFile() async {
// get the path to the document directory.
String dir = (await PathProvider.getApplicationSupportDirectory()).path;
print('DIR :::: ' + dir);
return new File(
'$dir/storage/files/file' + vars.newFileID.toString() + '.dat');
}
The error is:
Unhandled Exception: FileSystemException: Cannot open file,
path = '/data/user/0/com.example.ferenova_flutter_app/files/storage/file4.dat'
(OS Error: No such file or directory, errno = 2)
Thanks guys and take care !!! :)
While checking out path_provider examples, I ran into this important detail:
Directory directory = Platform.isAndroid
? await getExternalStorageDirectory() //FOR ANDROID
: await getApplicationSupportDirectory(); //FOR iOS
This solved my problem. I am no longer using default rootBundle asset to process any external files.
I think you should check your paths.
storage/fileList.txt is not the same as $dir/storage/files/file' + vars.loadedFileID.toString() + '.dat'
you should try:
response =
await rootBundle.loadString('$dir/storage/files/file' + vars.loadedFileID.toString() + '.dat'');
Or something similar.
GL