Flutter: issue with Future which block a function - flutter

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

Related

How can I send multiple async requests in Flutter that is not just concurrent but parallel?

I am calling a TTS API for multiple lines of texts. Ideally I would like to have them executed in parallel, meaning that one request would not wait for the other to finish before starting, hence speeding up total runtime of the API calls.
Here is an excerpt from my code:
await Future.forEach(rounds, (round) async {
await _fetchAudioForText(
round, voiceA );
totalBytes += round.audio?.lengthInBytes ?? 0;
});
...
Future<bool> _fetchAudioForText(Round round, Voice voice) async {
round.audio =
await ttsUtils.getAudioByteStream(round.text, voice);
return true;
}
getAudioByteStream is essentially calling the TTS API using a HTTP request and has a return type of a byte stream (Uint8List).
The problem is, that it's not actually doing it in parallel. What am I missing here?
remove await in forEach
await Future.forEach(rounds, (round) async {
_fetchAudioForText(
round, voiceA );
totalBytes += round.audio?.lengthInBytes ?? 0;
});

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 - How to save a file requesting permission with Permission Handler if my code is deprecated?

I'm new to Flutter and I've been following this tutorial but it's from 2020 and I know a lot of things have changed.
I want to save a file on my local phone and apparently I need to ask permission to do so. I'm not sure if checking the platform is still needed or not.
I don't want to run the --no-sound-null-safety command.
This is the only part that I wasn't able to update by myself.
_save() async {
if (Platform.isAndroid) {
await _askPermission();
}
var response = await Dio().get(widget.imgUrl!,
options: Options(responseType: ResponseType.bytes));
final result =
await ImageGallerySaver.saveImage(Uint8List.fromList(response.data));
print(result);
Navigator.pop(context);
}
_askPermission() async {
if (Platform.isIOS) {
await PermissionHandler().requestPermissions([PermissionGroup.photos]);
} else {
await PermissionHandler().checkPermissionStatus(PermissionGroup.storage);
}
}
The _save method seems alright but I wonder if there's something to update there as well.
The _askPermission method is the one that I need help with.

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.

Protractor tests are inconsistent even with browser.wait and helper functions

I am looking for some advice. I have been using protractor for a few weeks and just cannot get my tests to be consistent unless I use browser.sleep. I have tried helper functions as well as browser.wait(expectedCondition). I have reduced browser.sleep immensely, but protractor still just goes way to fast. I can never successfully run multiple tests unless I have a few browser.sleeps just so protractor can relax for a second. Here is an example:
The Test I need to select a user, delete that user and wait for a success message. Then I click the same user and click the activate button.
Outcome: Unless I have browser.sleep, my success messages do not even appear after deletion/activation. The tests fail because protractor is moving way too fast. Even with the expected conditions. My main problem is that protractor moves to fast for the angular web page. I have tried ifCLickable or isDisplayed but they do not fix the issue entirely. Here is the code:
async deleteUser() {
await sendClick(this.getNewUser());
await sendClick(this.getDelete());
await waitTillPresent(this.getDeleteConfirm());
await sendClick(this.getDeleteConfirm());
await waitTillPresent(this.getSuccessMsg())
expect(await page.getSuccessMsg().isDisplayed()).toBeTruthy();
}
async activateUser() {
await sendClick(this.getNewUser());
await waitTillPresent(this.getEditBtn())
await sendClick(this.getActive());
await waitTillPresent(this.getSuccessMsg())
expect(await page.getSuccessMsg().isDisplayed()).toBeTruthy();
}
Functions:
export async function sendClick(element: ElementFinder): Promise<boolean> {
try {
if(!await element.isDisplayed()) {
return false;
}
await browser.executeScript('arguments[0].click();', await element.getWebElement());
return true;
}
catch (err) {
return false;
}
}`
export async function waitTillPresent (element: ElementFinder, timeout: number = 10000) {
return browser.wait(() => {
return element.isPresent();
}, timeout);
}
My Question: Am I handling this correctly? Is there a better to ensure my tests are consistent? Before these tests, I visit a non-angular webpage. So I have to include the line browser.waitForAngularEnabled(false)
Does this mess with the async nature of angular? Thank you.
I worked the last few months on our e2e test suite to make it stable. I did not believe it's possible but I made it using correct wait functions and sometimes browser.sleep() as a last resort.
You have a correct approach for waiting for elements. But there are 2 problems regarding your implementation:
1) The function waitTillPresent() does exactly what its name stands for. But if you only wait until the element is present on the page it does not mean it's clickable or displayed. An element can be hidden and at the same time still be present. Please rename waitTillPresent() to waitTillDisplayed() and change it as follows:
export async function waitTillDisplayed(element: ElementFinder, timeout: number = 20000): Promise<boolean> {
let result = await browser.wait(() => element.isPresent(), timeout);
if(!result) {
return false;
}
return await browser.wait(element.isDisplayed(), timeout);
}
2) You should exceed the default timeout. Set it a bit higher like 20 to 25 seconds. Just play with it.
Unfortunately, I don't know how browser.waitForAngularEnabled(false) changes test behavior. We do not use it :)
Note:
These functions are all exactly the same:
function foo() { return 'hello world'; }
var foo = () => { return 'hello world'; };
var foo = () => 'hello world';
Play with arrow functions, it's syntactic sugar.
Cheers and gl!