Error: Cannot run with sound null safety, because the following dependencies (flutter_absolute_path) - flutter

Does the flutter_absolute_path v1.0.6 dependency not support null safety? Is there any other solution to get absolute path of a file?
for (int i = 0; i < resultList.length; i++) {
var path = await
FlutterAbsolutePath.getAbsolutePath(resultList[i].identifier);
print(path + '**path**');
f.add(File(path));
}

Try this plugin file_picker file_picker: ^3.0.3
//use this line to pick files
FilePickerResult? result = await FilePicker.platform.pickFiles();
//use this to process what file is returned
if(result != null) {
File file = File(result.files.single.path);
} else {
// User canceled the picker
}
You can read about extra features here:
https://pub.dev/packages/file_picker

Related

Flutter: Failed assertion: 'file.absolute.existsSync()': is not true

In my app, a user can send a file to others in a group chat. First, the user records some audio using their mic. The file is then touched up using FFMPEG. Then, the file is uploaded to Firebase Cloud Storage and if this is successful, a record is written in Firebase Realtime Database.
I'm getting the error below when the user records a long audio file and then presses submit. It almost seems as though FFMPEG hasn't finished processing the file...but I thought I used my async/await correctly to make sure that this processing is finished before moving on?
##MyAppFile## saveMyAppFileToCloudStorage Error: 'package:firebase_storage/src/reference.dart': Failed assertion: line 127 pos 12: 'file.absolute.existsSync()': is not true.
Psuedo-code:
User records audio
Audio file is processed using FFMPEG and the new processed file is created on the user's phone
User hits submit, uploading the file to Cloud Storage and, if successful, writing a record to Realtime Database
Order of Functions After User Hits Submit:
msgInput.dart -> sendMyAppFile()
msgInput.dart -> prepareMyAppFileForSending()
msgInput.dart -> runFFMPEGHighLow()
message_dao.dart -> sendMyAppFile()
message_dao.dart -> saveMyAppFileToCloudStorage() //ERROR COMES FROM THIS FUNCTION
The Code:
//msgInput.dart
Future<void> sendMyAppFile() async {
if (sendableMyAppFileExists == 1) {
final MyAppFileReadyToBeSent = await prepareMyAppFileForSending();
if (MyAppFileReadyToBeSent == '1') {
messageDao.sendMyAppFile(MyAppFile, filepath, filename);
} else {
}
}
setState(() {
sendableMyAppFileExists = 0;
});
}
Future<String> prepareMyAppFileForSending() async {
if (sendableMyAppFileExists == 1) {
if (recordedMyAppFileFilterID == '1') {
await runFFMPEGHighLow('1');
return '1';
}
if (recordedMyAppFileFilterID == '2') {
await runFFMPEGHighLow('2');
return '1';
}
}
return '0';
}
Future<void> runFFMPEGHighLow(String filterID) async {
if (filterID != '1' && filterID != '2') {
return;
}
if (sendableMyAppFileExists == 1) {
if (filterID == '1') {
await FFmpegKit.executeAsync(/*...parms...*/);
setState(() {
currentMyAppFileFilename = currentMyAppFileFilename + '1.mp3';
});
}
if (filterID == '2') {
await FFmpegKit.executeAsync(/*...parms...*/);
setState(() {
currentMyAppFileFilename = currentMyAppFileFilename + '2.mp3';
});
}
}
}
//message_dao.dart
void sendMyAppFile(ChatData MyAppFile, String filepath, String filename) {
saveMyAppFileToCloudStorage(filepath, filename).then((value) {
if (value == true) {
saveMyAppFileToRTDB(MyAppFile);
}
});
}
Future<bool> saveMyAppFileToCloudStorage(String filepath, String filename) async {
//filepath: /data/user/0/com.example.MyApp/app_flutter/MyApp/MyAppAudioFiles/MyAppFiles/2d7af6ae-6361-4be5-8209-8498dd17d77d1.mp3
//filename: 2d7af6ae-6361-4be5-8209-8498dd17d77d1.mp3
_firebaseStoragePath = MyAppFileStorageDir + filename;
File file = File(filepath);
try {
await _firebaseStorage
.ref(_firebaseStoragePath)
.putFile(file);
return true;
} catch (e) {
print('##MyAppFile## saveMyAppFileToCloudStorage Error: ' + e.toString()); //ERROR COMES FROM THIS LINE
return false;
}
return true;
}
I assume you're using the package ffmpeg_kit_flutter.
First, why it's not working: execute and executeAsync return FFmpegSession objects. The run of FFmpeg doesn't need to be finished for these methods to complete. In fact, the returned session object has methods like getState to monitor whether the run of FFmpeg has completed.
A good way to fix this: The documentation for executeAsync has a hint for what to do here.
Note that this method returns immediately and does not wait the execution to complete. You must use an FFmpegSessionCompleteCallback if you want to be notified about the result.
You can set a completion callback by passing a function to executeAsync. Here's the full function signature from the docs:
Future<FFmpegSession> executeAsync(
String command,
[FFmpegSessionCompleteCallback? completeCallback = null,
LogCallback? logCallback = null,
StatisticsCallback? statisticsCallback = null]
)
FFmpegSessionCompleteCallback is just a function that accepts an FFmpegSession and returns nothing. You can provide your own.
void someCompletionFunction() {
setState(() {
currentMyAppFileFilename = currentMyAppFileFilename + '1.mp3';
});
}
await FFmpegKit.executeAsync(/*...parms...*/, someCompletionFunction);
Future vs callback: If you prefer to use Futures and async-await instead of callbacks, you'll need to create your own Future and update it in the callback. See Dart, how to create a future to return in your own functions? for an example.

How do I make a separate folder for my app and store images there? Flutter

Hey guys so I wish to make a new folder for my app under Android and then store images there which will be retrieved for future use.
Here's what I have tried -
Future<bool> LocalImage(String url) async {
Directory directory;
var dio = Dio();
try {
if (Platform.isAndroid) {
if (await _requestPermission(Permission.storage) &&
// access media location needed for android 10/Q
await _requestPermission(Permission.accessMediaLocation) &&
// manage external storage needed for android 11/R
await _requestPermission(Permission.manageExternalStorage)) {
directory = (await getExternalStorageDirectory()) as Directory;
String newPath = "";
print(directory);
List<String> paths = directory.path.split("/");
for (int x = 1; x < paths.length; x++) {
String folder = paths[x];
if (folder != "Android") {
newPath += "/" + folder;
} else {
break;
}
}
newPath = newPath + "/Verif-ID";
if (await Directory(newPath).exists()){
;
}
else{ directory = Directory(newPath);
}
} else {
return false;
}
} else {
if (await _requestPermission(Permission.photos)) {
directory = await getTemporaryDirectory();
} else {
return false;
}
}
if (await directory.exists()) {
File saveFile = File(directory.path );
await dio.download(url, saveFile.path,);
return true;
}
} catch (e) {
print(e);
}
return false;
}
The issues I am currently facing are-
1.It does make a new folder by that name but no download is happening
2. I keep getting the following in my console
FileSystemException: Cannot create file, path = '/storage/emulated/0/Android/data/com.example.id_me/files' (OS Error: Is a directory, errno = 21)
I understand that this error must mean that a folder of the name already exists but I thought my if conditions were checking that but I guess not. My logics are not the best haha.
This is my first flutter app so I am very sorry if this was a silly doubt.
The URL will be from my firebase database.
Add a file name to a directory path.
savePath: The path to save the downloading file later. A path with String type, eg "xs.jpg"
await dio.download(url, saveFile.path + "/xs.jpg")
See download for details.

How can I get multiple messages from dart isolate?

How can I get multiple messages from dart isolate?
I'm trying to create an excel file and want to do some operation on that file in an isolate. Before doing an operation on that file, I want to return an message to main isolate, that excel file is created.
Here is function goes in isolate :
foo(String filePath){
// create excel file
var bytes = File(filePath).readAsBytesSync();
var excel = Excel.decodeBytes(bytes);
//HERE I WANT TO SEND THE MESSAGE THAT CREATING EXCEL FILE IS DONE
// some operatoin on excel file
var result = doSomeOperation(excel);
return result;
}
Main isolate code :
var result = await compute(foo, filePath);
What should I do to get creating file message before the actual result comes?
For excel, I'm using excel: ^2.0.0-null-safety-3 package.
Compute only returns one result. If you want to pass multiple 'events' back to the main isolate then you need to use the full Isolate logic (with sendPort and receivePort).
For example, the following code runs in an isolate, and downloads a file while emitting float values to represent progress, potentially a String to indicate log messages and then a bool to indicate success or failure upon completion.
Future<void> isolateDownload(
DownloadRequest request) async {
final sendPort = request.sendPort;
if (sendPort != null) {
var success = false;
var errorMessage = '';
var url = Uri.parse('a_url_based_on_request');
IOSink? out;
try {
http.StreamedResponse response =
await http.Client().send(http.Request('GET', url));
if (response.statusCode == 200) {
var filePath =
join(request.destinationDirPath, '${request.fileName}.ZIP');
var contentLength = response.contentLength;
var bytesLoadedUpdateInterval = (contentLength ?? 0) / 50;
var bytesLoaded = 0;
var bytesLoadedAtLastUpdate = 0;
out = File(filePath).openWrite();
await response.stream.forEach((chunk) {
out?.add(chunk);
bytesLoaded += chunk.length;
// update if enough bytes have passed since last update
if (contentLength != null &&
bytesLoaded - bytesLoadedAtLastUpdate >
bytesLoadedUpdateInterval) {
sendPort.send(bytesLoaded / contentLength);
bytesLoadedAtLastUpdate = bytesLoaded;
}
});
success = true;
if (contentLength != null) {
sendPort.send(1.0); // send 100% downloaded message
}
} else {
errorMessage =
'Download of ${request.fileName} '
'received response ${response.statusCode} - ${response.reasonPhrase}';
}
} catch (e) {
errorMessage = 'Download of ${request.chartType}:${request.chartName} '
'received error $e';
} finally {
await out?.flush();
await out?.close();
if (errorMessage.isNotEmpty) {
sendPort.send(errorMessage);
}
sendPort.send(success);
}
}
}
The code that spawns the isolate then simply checks for the type of the message passed to it to determine the action.
Future<bool> _downloadInBackground(
DownloadRequest request) async {
var receivePort = ReceivePort();
request.sendPort = receivePort.sendPort;
var isDone = Completer();
var success = false;
receivePort.listen((message) {
if (message is double) {
showUpdate(message);
}
if (message is String) {
log.fine(message); // log error messages
}
if (message is bool) {
success = message; // end with success or failure
receivePort.close();
}
}, onDone: () => isDone.complete()); // wraps up
await Isolate.spawn(isolateDownload, request);
await isDone.future;
return success;
}

Flutter : Conditions must have a static type of 'bool'

I'm trying to learn firebase with flutter and i ran into this problem
here is my code :
FirebaseFirestore.instance
.collection('attendees')
.doc(user.uid)
.snapshots()
.listen((snapshot) {
if (snapshot.data() != null) {
if (snapshot.data()!['attending']) {
_attending = Attending.yes;
} else {
_attending = Attending.no;
}
} else {
_attending = Attending.unknown;
}
notifyListeners();
});
what is the solution ?
the exact problem is within this line :
if (snapshot.data()!['attending']) {
how can I rewrite this so i wont ruin the current functionality ?
I appreciate your help inadvance
The reason you are getting error -
Conditions must have a static type of 'bool'
because on line snapshot.data()!['attending'] an = sign is missing.
To make your code work just do
if (snapshot.data() != snapshot.data()!['attending']) {
_attending = Attending.yes;
} else {
_attending = Attending.no;
}
Understanding The Error
I would also like to point out that Dart a stricter language (more like Java in terms of 'truthy' values).
In JavaScript you can use any ‘truthy’ value in a conditional statement. In Dart you cannot use ‘truthy’ values. For example:
var name = 'Joe';
if (name) {
// do something...
OR
var a = 1
if(a){
//this would work in JavaScript
}
You cannot do such things in Java or Dart. The reason is that Dart requires that a condition is a bool true not just a 'truthy' value. You could correct the code by changing it to:
if (name.isNotEmpty)
OR
if(a==1)
{
//these == signs are really important
}
Just store the snapshot.data() to the local map variable and do the operations by that.
_attendingSubscription = FirebaseFirestore.instance
.collection('attendees')
.doc(user.uid)
.snapshots()
.listen((snapshot) {
final Map<String, dynamic>? data = snapshot.data();
if (data != null) {
_attending = data['attending'] ? Attending.yes : Attending.no;
} else {
_attending = Attending.unknown;
}
notifyListeners();
});

Flutter/Dart: Saving an image file to "/storage/emulated/0/Picture/AppFolder/"

using
final dir = await getExternalStorageDirectory();
Image get saved in
/storage/emulated/0/Android/data/com.example.flutter_app/files/
But I want to store it in ../0/Pictures/app_name/ so that it shows up in the gallery.
I looked up all over the www and couldn't figure out. Please advise.
You have to first extract the root Path from the returned location
rootPath = /storage/emulated/0/
than create the Pictures and app_name Directory (to avoid exception when the directory doesn't exist)
then save file in /storage/emulated/0/Pictures/app_name/
here's a simple example to help you understand:
...
Directory externalPath = (await getExternalStorageDirectory());
String picturesDirName = "Pictures";
String appNameDirName = "app_name";
// Splitting the externalPath
List<String> externalPathList = externalPath.path.split('/');
// getting Position of 'Android'
int posOfAndroidDir = externalPathList.indexOf('Android');
//Joining the List<Strings> to generate the rootPath with "/" at the end.
String rootPath = externalPathList.sublist(0, posOfAndroidDir).join('/');
rootPath+="/";
//Creating Pictures Directory (if not exist)
Directory picturesDir = Directory(rootPath+picturesDirName+"/");
if (!picturesDir.existsSync()) {
//Creating Directory
await picturesDir.create(recursive: true);
//Directory Created
} else {
//Directory Already Existed
}
//Creating "app_name" Directory (if not exist)
Directory appNameDir = Directory(rootPath+picturesDirName+"/"+appNameDirName+"/");
if (!appNameDir.existsSync()) {
//Creating Directory
await appNameDir.create(recursive: true);
//Directory Created
} else {
//Directory Already Existed
}
//Creating String varible to store the path where you want to save file.
String fileSaveLocation = rootPath+picturesDirName+"/"+appNameDirName+"/";
// Or you can also use templates like this
String fileSaveLocation2 = "$rootPath$picturesDirName/$appNameDirName/";
//Your File Path where you want to save you file.
String filePath = fileSaveLocation+"text.txt";
// Or you can also use templates like this
String filePath2 = "${fileSaveLocation2}test.txt";
...
You can optimize the above code as per your liking.
hope this is the solution you were looking.
Here, Is how you can acheive this,
final Directory extDir = await getExternalStorageDirectory();
String dirPath = '${extDir.path}/app_name/pictures';
dirPath = dirPath.replaceAll("Android/data/com.example.flutter_app/files/", "");
await Directory(dirPath).create(recursive: true);
// start File Operations Here with dirPath variable
Update:
Along with detailed answer below, I came across a couple of plugins which deals specially with media content.
From Flutter.dev docs:
The plugin currently supports access to two file system locations:
gallery_saver Plugin: See Full Example
GallerySaver.saveImage(path).then((bool success) {// code }
Image_gallery_saver: See Full Example
await ImageGallerySaver.saveImage(Uint8List.fromList(response.data), quality: 60, name: "hello");
Creating the path manually: Source
await Permission.storage.request();
if (await Permission.storage.isGranted) {
var dir = await getExternalStorageDirectory();
Dio dio = Dio();
var newPath = "";
List<String> paths = dir.path.split("/");
for (int x = 1; x < paths.length; x++) {
String folder = paths[x];
if (folder != "Android") {
newPath += "/" + folder;
}
else {
break;
}
}
var picfolder = "/Pictures/";
newPath = newPath + picfolder + AppStrings['AppName'];
Other Useful References:
Android Data Storage
MediaStore in Flutter