Need to parallelise async operation using stream in dart - flutter

Problem: I am building an application on an RSS feed reader.
In a feed:
There are various items and each item is an article which has a URL.
Each URL has open graph metadata which needs to be fetched and takes time.
As soon as URL open graphs metadata is loaded it needs to be shown in the list on the UI.
Now I want to run 2 and 3 in parallel, am doing this in the code for now:
Stream<News> _getNewsRssFeed(Categories selectedCategory) async* {
try {
final rssUrl = _getRssUrl(selectedCategory);
RssFeed feed = await _getRssFeed(rssUrl);
if (feed.items != null) {
for (int itemIndex = 0; itemIndex < feed.items!.length; itemIndex++) {
final item = feed.items![itemIndex];
try {
Future<News> news = _processRssFeedItem(item, feed);
news.then((value){
yield value; // This is not working
});
} catch (error) {
print("Error while parsing ${item.link} error = $error");
}
}
}
} catch (error) {
print("Error while parsing RssFeed url $error");
}
}
The problematic line I have commented in above code, can you please let me know what is the best way here?

Basically, news.then returns Future and you are not awaiting for the result to be available.
You can change it by changing code to following
try {
final value = await _processRssFeedItem(item, feed);
yield value;
} catch (error) {
print("Error while parsing ${item.link} error = $error");
}

Related

getting null input in widget,data

I have an issue while writing my code
in the main page of my app i'm requesting a get from my json file and receiving a list of users + their info and when tapped on each of these users a new page opens up with some additional information.
in the 2nd page i make a new get request and this is where the problem happens and I get this error:
The following NoSuchMethodError was thrown building FutureBuilder(dirty, state: _FutureBuilderState get http
and my widget.data. returns as null
some help would be appreciated.
try {
String url =
'http://10.0.0.21:8000/api/users/' + widget.employee.id.toString();
individualData = await http.get(Uri.parse(url));
var test = individualData;
} catch (e) {
print(e.toString());
}
i forgot to put an if else to check if data exists
String? employeeId = widget.employee.id;
if(employeeId!=null){
try {
String url =
'http://10.0.0.21:8000/api/users/' + employeeId;
individualData = await http.get(Uri.parse(url));
var test = individualData;
} catch (e) {
print(e.toString());
}
}else{
print('Getting employeeId as null');
}
So first check employeeId is not getting a null and then call a API.

Writing Characteristics on Bluetooth Device

For those who have already worked on Bluetooth Low Energy on Flutter:
I am developing a mobile application. Simply, every time the user clicks on the "Read Data" button, I want to discoverServices and receive data:
onTap: () async {
deviceStateSubscription = bluetoothDevice.state.listen((s) {
if (s == BluetoothDeviceState.connected) {
_discoverServices();
} else {
bluetoothDevice.connect();
_discoverServices();
}
});
},
I have realized that connection is sometimes dropping for no reason. That is why I use an 'if/else' to check the connection.
Here is my discoverServices method:
_discoverServices() async {
List<BluetoothService> services = await bluetoothDevice.discoverServices();
if (bluetoothServices != null && bluetoothServices.isNotEmpty) {
bluetoothServices.forEach((service) {
if (service.uuid.toString().toLowerCase() == Configuration.nusUUID) {
service.characteristics.forEach((c) async {
if (c.uuid.toString() == Configuration.txUUID) {
txChar = c;
try {
await txChar!.write(Configuration.config, withoutResponse: true); // Sending the configuration
await txChar!.write(Configuration.rfCmdStart, withoutResponse: true); // Sending the start configuration
await txChar!.write(Configuration.rfCmdStop, withoutResponse: true); // Sending the stop configuration to say that I am done, so send me data now
} catch (e) {
print(e.toString());
}
} else if (c.uuid.toString() == Configuration.rxUUID) {
rxChar = c;
rxChar!.setNotifyValue(true);
rxChar!.value.listen((value) async {
if (value.isEmpty) {
print('No data received');
} else {
print(value);
}
});
}
});
}
});
}
}
The problem is, the button sometimes works and sometimes doesn't... It is not stable at all. Sometimes when I click, I receive data; and sometimes I receive Unhandled Exception: PlatformException(set_notification_error, error when writing the descriptor, null, null). I couldn't find any logical answers for that. To fix it, I am simply clicking on the button a few more times so that it sends me the data.
Any thoughts about how to fix this descriptor error?
writing the txChar characteristic with withoutResponse: true three times in a row is a problem. As the documentation for the write method states:
Writes the value of a characteristic.
CharacteristicWriteType.withoutResponse: the write is not guaranteed
and will return immediately with success.

How to show firebase storage file upload progress

I am trying to show the progress of the file I am uploading to firebase storage in a progress indicator how can I achieve this.?
Note that I am a uploading two files at the same time.
here is my code.
Future<String?> _uploadFileToStorage(BuildContext context,File file, path) async {
try {
var task = _firebaseStorage.ref().child(path);
var status = await task.putFile(file);
print(status.state);
return task.getDownloadURL();
} catch (err) {
print(err.toString());
}
}
first take variable bool load and make initialy load=false when your function start make load=trueand at then end of your function load=false, so on your parent widget load==true?CircularProgressIndicator():Scaffold(),
so here you function
Future<String?> _uploadFileToStorage(BuildContext context,File file, path) async {
try {
setState({
load=true;
});
var task = _firebaseStorage.ref().child(path);
var status = await task.putFile(file);
print(status.state);
return task.getDownloadURL();
setState({
load=false;
})
} catch (err) {
print(err.toString());
setState({
load=false;
})
}
}
You can track the uploading progress by simply using the following code:
status.snapshotEvents.listen((TaskSnapshot snapshot) {
print('Task state: ${snapshot.state}');
print('Progress: ${(snapshot.bytesTransferred / snapshot.totalBytes) * 100} %');
switch (snapshot.state) {
case TaskState.paused:
// TODO: Handle this case.
break;
case TaskState.running:
return CircularProgressIndicator(
value: (snapshot.bytesTransferred > 0 && snapshot.totalBytes > 0) ? snapshot.bytesTransferred / snapshot.totalBytes : null);
break;
case TaskState.success:
// TODO: Handle this case.
break;
case TaskState.canceled:
// TODO: Handle this case.
break;
case TaskState.error:
// TODO: Handle this case.
break;
}
}, onError: (e) {
// The final snapshot is also available on the status via `.snapshot`,
// this can include 2 additional states, `TaskState.error` & `TaskState.canceled`
print(status.snapshot);
});

Can a Future internally retry an http request if it fails in Flutter?

I'm using the following code to successfully poll mysite for JSON data and return that data. If the request fails, then it successfully returns an error message as well return Result.error(title:"No connection",msg:"Status code not 200", errorcode:0);.
What I'd like to happen is have the app retry the request 3 times before it returns the error message.
Basically have the future call itself for some number of iterations.
I did try to create an external function that would get called from the outer catch which then would in turn call the getJSONfromTheSite function a second and then a third time, but the problem was that you would have a non-null return from the Future so the app wouldn't accept that approach
Is there another way of doing this?
Future<Result> getJSONfromTheSite(String call) async {
debugPrint('Network Attempt by getJSONfromTheSite');
try {
final response = await http.get(Uri.parse('http://www.thesite.com/'));
if (response.statusCode == 200) {
return Result<AppData>.success(AppData.fromRawJson(response.body));
} else {
//return Result.error("Error","Status code not 200", 1);
return Result.error(title:"Error",msg:"Status code not 200", errorcode:1);
}
} catch (error) {
return Result.error(title:"No connection",msg:"Status code not 200", errorcode:0);
}
}
The following extension method will take a factory for futures, create them and try them until the retry limit is reached:
extension Retry<T> on Future<T> Function() {
Future<T> withRetries(int count) async {
while(true) {
try {
final future = this();
return await future;
}
catch (e) {
if(count > 0) {
count--;
}
else {
rethrow;
}
}
}
}
}
Assuming you have a rather plain dart method:
Future<AppData> getJSONfromTheSite(String call) async {
final response = await http.get(Uri.parse('http://www.thesite.com/'));
if (response.statusCode == 200) {
return AppData.fromRawJson(response.body);
} else {
throw Exception('Error');
}
}
You can now call it like this:
try {
final result = (() => getJSONfromTheSite('call data')).withRetries(3);
// succeeded at some point, "result" being the successful result
}
catch (e) {
// failed 3 times, the last error is in "e"
}
If you don't have a plain method that either succeeds or throws an exception, you will have to adjust the retry method to know when something is an error. Maybe use one of the more functional packages that have an Either type so you can figure out whether a return value is an error.
Inside catch() you can count how many times have you retried, if counter is less than three you return the same getJSONfromTheSite() function, but with the summed counter. And if the connection keeps failing on try{} and the counter is greater than three it will then returns the error.
Future<Result> getJSONfromTheSite(String call, {counter = 0}) async {
debugPrint('Network Attempt by getJSONfromTheSite');
try {
String? body = await tryGet();
if (body != null) {
return Result<AppData>.success(AppData.fromRawJson(response.body));
} else {
//return Result.error("Error","Status code not 200", 1);
return Result.error(title:"Error",msg:"Status code not 200", errorcode:1);
}
} catch (error) {
if(counter < 3) {
counter += 1;
return getJSONfromTheSite(call, counter: counter);
} else {
return Result.error(title:"No connection",msg:"Status code not 200", errorcode:0);
}
}
}

Post Api not return any response in nest js

I use nestjs and psql and I want upload files and save the url in the database . when I run the api , data save on db but it doesn’t return any response .
this is my service:
async uploadFiles(files){
if (!files) {
throw new HttpException(
{
errorCode: UploadApplyOppErrorEnum.FileIsNotValid,
message: UploadApplyOppMsgEnum.FileIsNotValid,
},
HttpStatus.UNPROCESSABLE_ENTITY,
);
}
const filedata = OrderFilesData(files);
return filedata.map(async(filePath) => {
let orderFile = new OrderFile();
orderFile.fileUrl = filePath.fileUrl;
orderFile.type = filePath.fileType;
try {
let result = await this.orderFileRepository.save(orderFile);
return await result
} catch (error) {
throw new BadRequestException(error.detail);
}
});
}
and this is my controller
#UploadOrderFilesDec()
#Post('upload')
uploadFiles(#UploadedFiles() files){
return this.ordersService.uploadFiles(files);
}
You can't return an array of async methods without using Promise.all(), otherwise the promises haven't resolved yet. You can either use return Promise.all(fileData.map(asyncFileMappingFunction)) or you can use a regular for loop and await over the results.