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

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!

Related

How to handle reading from database when API-request hasn't finished yet to save to database in 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.

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 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()
}
}

Flutter: Best practice for nested async HTTP calls

I'm trying to load the result of two Http requests into a list-item (the second request is a get detail about the first request result item).
The first request gets a list of cryptocurrencies.
The second request collects more information about that currency.
The reason why I want to accomplish this in two separate requests is,
the data is on different sources.
after I got the list of currencies I want to show a process circle, which means "stay calm, details are loading"
What I already tried is ;
One stateful-widget with two methods ( both are calling setState) it was catastrophic. Endless loop.
One base stateful-widget and a second child stateful-widget in the build method. Worked but whenever I scroll up or down, the list re-renders its items, and the child stateful-widget makes the same getPrice HTTP request again and again.
One inherited-widget and one stateless-widget. Had to use the context for both of the HTTP request methods. It caused endless loops again.
Currently, a part of my Brain yells "Redux is the only way to handle this in a proper way"
Do you have any suggestions how to handle this?
Thank you in advance.
void getOwnedCoins() async {
CoinsList cList = await ResourceCoins().list();
setState(() {
list = cList;
items = cList.result.length;
});
getCoinSummary(cList);
}
void getCoinSummary(CoinsList coins) {
coins.result.forEach((Coin coin) async {
CoinSummary cs = await ResourceCoinSummary().summary(market: {'market': 'btc-' + coin.currency});
coin.summary = cs;
setState(() {
list = coins;
});
});
}
That's how I solved it. My mistake was to call the getCoinSummary in the ListView.builder. However, I feel not comfortable with that setState in getCoinSummary. Every time I get details from server I re-render the whole list. Is it how it should be?

Vala force refresh progressbar

I've made an aplication with vala where at some point I have to process a lot of files. I've created a window to choose a folder and then I get the paths of files and make some proces on them.
I've added a progress bar to this window to show how many files have been processed but for some reason it remains always empty.
Code about window:
this.files_window = new Gtk.Window();
this.files_window.window_position = Gtk.WindowPosition.CENTER;
this.files_window.destroy.connect (Gtk.main_quit);
// VBox:
Gtk.Box vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 5);
this.files_window.add (vbox);
// Buttons to open and close
Gtk.Button cancel = new Gtk.Button.with_label ("Cancel");
Gtk.Button select = new Gtk.Button.with_label ("Select");
vbox.add (select);
vbox.add (cancel);
// proogress bar
this.progress_bar = new Gtk.ProgressBar();
vbox.add(this.progress_bar);
// conect select to method do_stuff
select.clicked.connect (do_stuff);
this.files_window.show_all ();
As you can see, I connect the button "select" to the method "do_stuff" where I get the paths of selected files and make some process.
I update correctlly the fraction of the progres bar because I've added some prints to know if the value is correct and it is. It's just that the windows is not refreshing, possibly because all the work it is doing with the process of the files. Here is the code about do_stuff() method:
// some proces to get paths of files in the list sfiles
double fraction = 0.0;
this.progress_bar.set_fraction (fraction);
int processed_files = 0;
foreach (string sfile in sfiles) {
do_some_proces_to_file(sfile);
processed_files += 1;
fraction = (double)processed_files/(double)sfiles.length;
this.progress_bar.set_fraction (fraction);
stdout.printf("Real fraction: %f\n", this.progress_bar.get_fraction());
}
The printf shows that the value of the progres bar is being updated but in the window the bar is always empty.
Am I missing something? Is it the correct way to do the progres bar? Should I made another thread to do the stuff?
As #nemequ says, your code is blocking the main loop thread (which handles both user input and scheduling/drawing widget updates), hence it the progress bar is not updated until the method completes.
Using a thread is one way solve the problem, however using threads can lead to a lot of bugs however since it can be difficult to make even simple interactions between threads safe.
An async method avoids this by interleaving the code with the other work being done by the main loop. An async version of your do_stuff() would be pretty straight-forward to write, simply declare it async and put a yield in the for loop somewhere:
public async void do_stuff() {
...
foreach (string sfile in sfiles) {
// all of this is as before
do_some_proces_to_file(sfile);
processed_files += 1;
fraction = (double)processed_files/(double)sfiles.length;
this.progress_bar.set_fraction (fraction);
// Schedule the method to resume when idle, then
// yield control back to the caller
Idle.add(do_stuff.callback);
yield;
}
}
You can then kick it off from your click handler by calling: do_stuff.begin().
Unless there is some relevant code you're not showing, you're blocking the main loop. One option would be to do everything in a thread, and use an idle callback to update the UI. The basic idea is something like:
new GLib.Thread<void*>("file-processor", () => {
foreach (string sfile in sfiles) {
/* do stuff */
GLib.Idle.add(() => {
/* Update progress */
return false;
});
}
return null;
});
Depending on your application you may need to add a mutex to avoid race conditions. You may also need to add some logic for canceling the operation.
A better option might be to use a GLib.ThreadPool. You'd still want to update the UI from an idle callback, but this would allow each task to execute in parallel, which could provide a significant speed-up.
If I were you I'd probably wrap it all up in an async function to keep the API tidy, but you don't really have to.