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

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.

Related

Flutter null or emptylist

it's just a small question about dart/flutter code.
I saw this code today :
Future<List<String>?> getAtSignListFromKeychain() async {
var atsignMap = await _getAtSignMap();
if (atsignMap.isEmpty) {
// no atsigns found in biometric storage
// read entries from flutter keychain
// for mobile platforms only
if (Platform.isAndroid || Platform.isIOS) {
atsignMap = await checkForValuesInFlutterKeychain();
if (atsignMap.isEmpty) {
return null;
}
} else {
return null;
}
}
var atsigns = atsignMap.keys.toList();
_logger.info('Retrieved atsigns $atsigns from Keychain');
return atsigns;
}
I don't understand interest of returning null with List? . Isn't better to write this code ?:
Future<List<String>> getAtSignListFromKeychain() async {
var atsignMap = await _getAtSignMap();
if (atsignMap.isEmpty) {
// no atsigns found in biometric storage
// read entries from flutter keychain
// for mobile platforms only
if (Platform.isAndroid || Platform.isIOS) {
atsignMap = await checkForValuesInFlutterKeychain();
if (atsignMap.isEmpty) {
return atsignMap;
}
} else {
return List.empty();
}
}
var atsigns = atsignMap.keys.toList();
_logger.info('Retrieved atsigns $atsigns from Keychain');
return atsigns;
}
Or I'm missing something ? Thank you !
First of all, there are always different solutions for different problems. I believe it is better to return a null value for some cases instead of creating an empty list in the memory and returning that. Either way, you have to check the returning value, handle errors, etc. So instead of checking if the list is empty or not, you can use just the ?? operator.
And in some cases, the list you expect with the items in it may be empty. If you return an empty list by default in such cases, you would not know if the list that you expected is really empty or is there a problem.

Flutter flutter_blue label printer issue

I am new in flutter.
I am building an app to print on the ble label printer via Bluetooth.
I use flutter_blue library. I had successfully connected to the device and found the target characteristic. My problem is that when I call write() function of the characteristic printer show on display “waiting for data” and nothing is printed.
Could you please help me with the data format I need to put into the method?
I use utf8 encoded string as data.
Future<void> _print() async {
var serviceUuid = '0000ff00-0000-1000-8000-00805f9b34fb';
var characteristicsUuid = '0000ff02-0000-1000-8000-00805f9b34fb';
_services.forEach((service) {
if(service.uuid.toString().toLowerCase() == serviceUuid) {
printService = service;
for (var c in printService!.characteristics) {
if(c.uuid.toString().toLowerCase() == characteristicsUuid) {
var ZPL_TEST_LABEL = 'Hello WeChat!\r\n';
printCharacteristic = c;
writeData(printCharacteristic, ZPL_TEST_LABEL);
}
}
}});
}
Future<void> writeData(characteristic, data) async{
if (characteristic == null) return;
try {
var utf8Encoded = utf8.encode(data);
debugPrint(data.toString());
var result = await characteristic.write(utf8Encoded);
debugPrint(result.toString());
} catch (e) {
debugPrint(e.toString());
}
}
My first step is to print at least a string, could you pls help me?

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;
}

The input does not contain any JSON tokens (Blazor, HttpClient)

i have an http Get method like below
public async Task<Ricetta> GetRicettaByNome(string nome)
{
Ricetta exist = default(Ricetta);
var ExistRicetta = await appDbContext.Ricetta.FirstOrDefaultAsync(n => n.Nome == nome);
if(ExistRicetta != null)
{
exist = ExistRicetta;
return exist;
}
exist = null;
return exist;
}
It gets called by a controller like this:
[HttpGet("exist/{nome}")]
public async Task<ActionResult<Ricetta>> GetRicettaByNome(string nome)
{
try
{
if (string.IsNullOrEmpty(nome))
{
return BadRequest();
}
var result = await ricetteRepository.GetRicettaByNome(nome);
if (result != null)
return result;
return default(Ricetta);
}
catch (Exception)
{
return StatusCode(StatusCodes.Status500InternalServerError, "NON HAI INTERNET!");
}
}
But when i call my api to get the resposne by an httpclient like this:
public async Task<Ricetta> GetRicettaByNome(string nome)
{
return await httpClient.GetJsonAsync<Ricetta>($"api/Ricette/exist/{nome}");
}
i got this error:
the input does not contain any JSON tokens. Expected the input to start with a valid JSON token, when isFinalBlock is true. Path: $ | LineNumber: 0 | BytePositionInLine: 0.'
This is the expected result when you return null from your API. And default(Ricetta) is the same as null.
You will have to handle this some other way. GetJsonAsync<T>() is convenient shorthand when you know you will always have data. It is not the best option for dealing with null.
You can see (in dev tools) that the status code is 204 (No Content) for null. You can detect that or catch the error from GetJsonAsync.
Your error exist in your repository part where GetJsonAsync<>. You need to use HttpResponseMessage and check the content before Deserialize for example:
private async ValueTask<T> GetJsonAsync(string ur)
{
using HttpResponseMessage response = awiat _client.GetAsync(url);
//some method to validate response
ValidateResponse(response);
//then validate your content
var content = await ValidateContent(response).ReadAsStringAsync();
return JsonSerializer.Desrialize<T>(content, new JsonSerializerOptions() { PropertyNameCaseInsensitive = true });
}
//Here is the method that you need
private HttpContent ValidateContent(HttpResponseMessage response)
{
if(string.IsNullOrEmpty(response.Content?.ReadingAsString().Result))
{
return response.Content= new StringContent("null",Encoding.UTF8, MediaTypeNames.Application.Json);
}
else
{
return response.Content;
}
}

block of coding running twice

So am having a scroll listener to get more data now the problem is that when the listener calls the function for more data then am having 4 if conditions but the function is running the if condition twice i don't understand the logic behind it.
MyCode:-
void _scrollListener()async{
if (!loading) {
if (scrollController.position.pixels == scrollController.position.maxScrollExtent) {
setState(() => load = true);
await clearEmptyDocs();
getMoreData();
}
}
}
getMoreData()async
{
var quicks;
var posts;
if(publicPostsDocuments.isNotEmpty)
{
print('1\n\n\n\n');
posts=await getPublicPosts();
}
if(publicQuicksDocuments.isNotEmpty)
{
print('2\n\n\n\n');
quicks=await getPublicQuicks();
}
if(publicPostsDocuments.isEmpty)
{
print('3\n\n\n\n');
posts=await getPublicDocuments();
}
if(publicQuicksDocuments.isEmpty)
{
print('4\n\n\n\n');
quicks=await getPublicDocumentsForQuicks();
}
setState(() {
load=false;
});
}
Here am having four lists and based on them am having if blocks so when lists are not empty then it should simply print 1 and 2 and then exit but it is printing 1,2,1,2 i.e. first 2 if blocks are running twice.