How to handle reading from database when API-request hasn't finished yet to save to database in Flutter? - flutter

For my App i'm loading the link for the background-image for each screen from my API.
Right after the query that downloads and saves the image-link, i'm querying the link from the database.
The problem now is, that the function isn't waiting for the download and saving to finish although i'm using await, therefor i get an empty result from the database and get an error from the imageloader.
Future<String> downloadAsset(String name) async {
final Map<String, String> _json = {
'mandant': Config.mandant,
'name': name
};
final query = MakePost('get_app_assets', false, _json, saveAsset);
await query.queryAPI(); // Function won't wait for this to finish
String ret = await dbAppAssets.getText(name); // Get link from database
return ret;
}
I've already tried to use .then(), but the same thing happens.
This only happens initially on the first call of each screen, but how is this normally beeing handled?
I'm using riverpod with futureProviders if that matters.

I do not know where you are using the downloadAsset function, but when you use Future on a function you should also await the function where you are using it, for example:
Future<void> _reverseScrollToTopAnimation() async {
await controller!.reverse();
_showBackToTopButton = false; // hide the back-to-top button
}
then, wherever you call it you should also await that function:
await _reverseScrollToTopAnimation();
If not the function will act as a synchronous operation even though you are using await inside the function.

Related

How to restrict the user from creating more than one doc in firestore database?

I have a form on my flutter app where the userr can submit his personal info:
try {
await FirebaseFirestore.instance
.collection('users')
.doc(userID).collection('personalInfo')
.add(_insertProfInfoToDBToDB.toJson());
This function is triggered by a sunmit button, now the problem is that if the user doesnt have an internet connection for example and he submit this form, then he will get an error that there is no internet connection which is coming from another function
Future<void> _tryConnection() async {
try {
final response = await InternetAddress.lookup('Example.com');
setState(() {
_isConnectionSuccessful = response.isNotEmpty;
});
} on SocketException catch (e) {
print(e);
setState(() {
_isConnectionSuccessful = false;
});
}
if (_isConnectionSuccessful != true){
Utils.showSnackBar("Please check your internet connection");
}
}
but this doesnt restric him from pressing the submit button again and again and again...
So what is happening is that once the user gets an internet connection, there will be multiple docs created at the same time inside the 'personalInfo' collection.
My question is how to restrict the user from creating more than one doc inside a collection?
Use setData
Firestore.instance.
collection('users').
document('userID').collection('collection name').document('docId')
set(data, merge: true)
Now this will create a new entry if it doesnt exist and update it if its existing
From the docs
setData(Map<String, dynamic> data, {bool merge: false}) → Future
Writes to the document referred to by this DocumentReference. If the document does not yet exist, it will be created.
.If merge is true, the provided data will be merged into an existing document instead of overwriting.>

Flutter multi threading save file to local

for(var item in list)
{
var body = (await api.get(item.url)).bodyBytes;
await file.writeAsBytes(body);
}
How can I change this code to multi threading. This code is very slow. Any examples?
It's slow because you're forcing your application to wait for every api request and every file write before starting the next iteration.
Start every request at the same time and wait for them all simultaneously. Multithreading will not speed up tasks that are just slow and it would not be practical in Dart.
await Future.wait(list.map((item) async {
var body = (await api.get(item.url)).bodyBytes;
await file.writeAsBytes(body);
}));
Or with a more verbose, but maybe clearer syntax:
List<Future> futures = [];
for(var item in list) {
futures.add(Future(() async {
var body = (await api.get(item.url)).bodyBytes;
await file.writeAsBytes(body);
}));
}
Future.wait(futures);
I'm not really sure why you're doing await file.writeAsBytes(body); with the same file every iteration, but it appears to be a mistake to me. Just be aware.

Flutter: issue with Future which block a function

I am currently developping a flutter application with Dart. I want to add some local notification when there is something new.
For that I'm using a periodic task with workmanager, which make a http request to check the last "news". The issue here is that the function stop at client.get()
Future<List<String>> _grepLastNews() async {
var client2 = new http.Client();
debugPrint("here");
//issue below
final response =
await client2.get('http://..../lireLastNews.php').timeout(Duration(seconds: 4));
client2.close();
debugPrint("here 2");
var body;
if (response.statusCode == 200) {
body = jsonDecode(response.body);
} else {
return ["0","0"];
}
return jsonDecode(body);
}
Here you can find the output:
output
You can see it stop before the second checkpoint... I have tried with and without timeout, with changing the name of the other http client of the application but nothing work. I must add that I have an other http client which work perfectly ( but not in background ^^).
Thanks for helping me
EDIT: I tried with
await Future.delayed(Duration(seconds: 2),() => ["0","0"]);
but the output is the same so the issue is not with http.get but about the future which I don't know why stop there.
EDIT 2: In fact in an async function I tried
debugPrint("here 2");
await Future.delayed(Duration(seconds: 2));
debugPrint("here 3");
and it never go for "here 3".
EDIT 3:
I tried differents variant using Future.wait([_grepLastNews()]); but it don't work: it continue until raising an error because of the null result of _grepLastNews().

How can I asynchronously stream loaded objects from a list of futures in Dart

I have a list of objects that can be loaded by calling the function object.load().
This function returns a loaded version of the same object asynchronously.
I want call the load() funcion of all objects in a list at the same time and stream the loaded versions as soon as they finish loading.
The code below works but the program is loading one object at a time.
Sender:
Stream<ImageIconModel> streamLoadedIcons() async* {
for (var i = 0; i < imageIconModels.length; i++) {
yield await imageIconModels[i].load().then((loadedIconModel) {
return loadedIconModel;
});
}
}
Receiver:
await for (var loadedIcon in streamLoadedIcons()) {
final var result = doSomething(loadedIcon);
yield result;
}
The main problem is:
In the sender, if I await each load() call, it will do every step in the loop awaiting the load() to finish.
But if I remove the "await", I would be returning a future, not the loaded icon.
You need Stream.fromFutures.
final loadedIconModelStream = Stream.fromFutures([
for (final iconModel in imageIconModels) iconModel.load(),
]);
Both #hacker1024 an #pskink answers successfully answered my question!
But neither one worked as it supposed to and I think I discovered why.
I substituted the load() method for a Future.delayed(duration: random), and then the program worked as it intended to.
So what I think happened is that probably the lib I'm using to load the images (multi_image_picker: ^4.7.14) is accessing the phone files synchronously.
So even if I try to load every image at same time, it will do the task synchronously and return every image at the order I called them to load.
Thank you both for the answer!

Flutter Await for websocket response

Solved
I've solved this problem using a Future function and using Completer inside the function.
I am getting some Images from my server with websockets.
I have a function that emits an event with the name of the image that I need and sends an event with the Image, that is working fine, but I need my App to wait for the return of my function because my app is calling the function and trying to go to the next page without the image.
Can someone explain to me how can I make my app wait for my function to return ?
Update
I am using Stream Builder now but I can't return the data from my function.
Stream<List> getFile(List imageL) async*{
if(imageL.isNotEmpty){
List downloadedData = List();
socket.emit("PostsImagem", (imageL));
socket.on("ImagemPost", (dados) {
downloadedData = dados;
imageL = List();
});
//I can't return the downloadedData because is inside the //socket.on()
}
}