Dart check uncompleted Future - flutter

I want to check if a Future<dynamic> is not yet completed after 500 millis.
RaisedButton(
onPressed: () async {
setState(() {
isLoading = true;
});
dynamic responseBody;
Future.delayed(Duration(milliseconds: 500), () {
print(responseBody == null); // always false, so it never null, even uncompleted
// if (responseBody != null) {
// isLoading = false;
// }
});
NetworkHelper n = NetworkHelper(queryFinalUrl);
responseBody = await n.getData();
},
)
import 'package:http/http.dart' as http;
import 'dart:convert';
class NetworkHelper {
NetworkHelper(this.url);
final String url;
Future getData() async {
http.Response response = await http.get(this.url);
String responseBody = response.body;
return jsonDecode(responseBody);
}
}
I tried to check my dynamic is null and responseBody == null , but it seems never null.
Update for details:
OK, I have a loading indicator / loading spinner which will cover fullscreen using Visibility() which I need to display once I pressed the button. So you might noticed the code isLoading is the visibility bool. So my idea is , I want to add on another timer counter Future.delayed immediately once button is pressed, so that is counts for 500 millis, and if:
500ms has finished, and responseBody is not received yet , continue display the loading indicator
500ms has finished and responseBody is completed, dismiss the loading indicator
Let's say responseBody completed in 400ms, Loading indicator must persist for another 100ms before it can be dismissed
I hope this details is clear for you. :) Sorry for inconvenience.
Yes, I'm aware I need to put an await while waiting for fetching web data
I show the loading indicator by setState()
So to conclude, this is why I wanna check if a Future<dynamic> is completed or uncompleted

Your responseBody == null check never succeeds because you do:
responseBody = n.getData();
which unconditionally assigns responseBody a Future. responseBody therefore will always be assigned a Future, regardless of whether it's complete or not. You instead could do:
responseBody = await n.getData();
which will assign responseBody the value of the Future only after the Future completes.
Alternatively, you could use Future.timeout:
NetworkHelper n = NetworkHelper(queryFinalUrl);
try {
dynamic responseBody = await n.getData().timeout(Duration(milliseconds: 100));
...
} on TimeoutException {
isLoading = false;
}

Related

setState() called after dispose() error after calling async function

I have this button that uploads to Firestore a picture that the user selects and stores the picture url into a varialble to be used to update the user's information.
SELECTION BUTTON calls selectFile().
// SELECTING FILE FOR UPLOAD
Future selectFile() async {
final result = await FilePicker.platform
.pickFiles(allowMultiple: false, type: FileType.image, withData: true);
if (result == null) return;
setState(() {
pickedFile = result.files.first;
texto = Text(pickedFile!.name);
});
}
This successfully changes the state of pickedFiles and Texto variable.
Then I have this other button later in the code that calls uploadFile() and then exits the page with navigator.pop(context).
// UPLOADING FILE AND RETRIEVING DOWNLOAD LINK
Future uploadFile() async {
var fileBytes = pickedFile?.bytes;
var fileName = pickedFile?.name;
var ref = FirebaseStorage.instance.ref().child('UserImages/$fileName');
if (fileBytes == null) {
return '';
}
TaskSnapshot uploadedFile = await ref.putData(fileBytes);
url = await ref.getDownloadURL();
log(url);
if (uploadedFile.state == TaskState.success) {
setState(() { <<<<<<<<--------- setState() called after dispose() ERROR HERE
_petImage = url;
});
}
}
The function does upload the picture to FireStore and even produces a link (tested by using log(url)) but when it reaches the set state it fails.
I have no idea why this is not updating the state of the _petImage variable which stored outside of the main build(context) together with the other variables suck as pickedFile and texto. the setState work fine in other functions but in this function is not working .
what could I be doing wrong here?
It is safe to check if the state is mounted on async and then perform setState.
_() async {
if (mounted) {
setState(() {});
}
}

Is there a way to skip await if it's too long? (Flutter)

I use async await in main so the user has to wait in the splash screen before entering the app.
void main() async {
await Firebase.initializeApp();
String? x;
await FirebaseDatabase.instance.ref().child("data").once().then((snapshot) {
Map data = snapshot.snapshot.value as Map;
x = jsonEncode(data);
});
return ChangeNotifierProvider<DataModel>.value(
value: DataModel(data: x),
child: MaterialApp()
);
}
If there are users entering the app without an internet connection, they will be stuck on the splash screen forever. If there are also users with slow internet connection, they will be stuck on the splash screen longer.
So no matter what the internet connection problem is, I want to set a maximum of 5 seconds only to be in the await, if it exceeds, skip that part and go straight into the app.
Pass your Firebase api call to a function, make the method return future,
Future<String?> getData() async {
await FirebaseDatabase.instance.ref().child("data").once().then((snapshot) {
Map data = snapshot.snapshot.value as Map;
return jsonEncode(data);
});
}
Then on main method, attach a timeout with that method you created
void main() async {
await Firebase.initializeApp();
String? x = await getData().timeout(Duration(seconds: 5), onTimeout: () {
return null };
return ChangeNotifierProvider<DataModel>.value(
value: DataModel(data: x),
child: MaterialApp()
);
}
So if you api takes more then 5 seconds of time, then it exit and return null
you can User Race condition or Future.timeout
Race condition using future.any
final result = await Future.any([
YourFunction(),
Future.delayed(const Duration(seconds: 3))
]);
in above code one who finish first will give result,
so if your code takes more than 3 sec than other function will return answer

making new request in dio by removing first request

I have a function which makes a api call, if i call that api twice, i want to cancel first call i'm trying to use dio for this
import 'package:dio/dio.dart';
final _dio = Dio();
CancelToken abc = CancelToken();
Future<void> makeSearchQuery(String searchTerm) async {
abc.cancel();
return _dio.get(searchTerm, cancelToken: abc).then((value) {
print(value);
}).catchError((e) {});
}
void main() async {
await makeSearchQuery("https://reqres.in/api/products/3");
await makeSearchQuery("https://reqres.in/api/products/3");
}
The problem is i'm not getting any response for either of that, though i should get the response for second call
P.S actually I'm calling this function in onPressed of a button if that matters.
What you need is a debouncer,
class Debouncer {
final int milliseconds;
Timer? _timer;
Debouncer({
this.milliseconds = 500,
});
void run(VoidCallback action) {
if (_timer != null) {
_timer?.cancel();
}
_timer = Timer(Duration(milliseconds: milliseconds), action);
}
}
use run method to call your function.
when timer gets canceled your function won't get called

how do i force Flutter to run all the lines inside the function?

I have a this function that I need to run during initstate() in its entirety before building the widget. I have used this kind of code before in some parts of my app and it works there, but in this case, flutter jumps out before executing .then , goes to build the widget tree, and then returns back to the function but skips the remaining lines. I'm confused where I should properly put async-awaits to force it to finish the block. Also, can I ask where I can read an explanation of the proper flow of execution for flutter so that I can understand it more?
Future <bool> checkVendorStatus (buyerId) async {
var _result;
var vendorDocRef = await buyersInfoColl.doc(buyerId)
.collection("vendorsCalled")
.doc(auth.currentUser!.uid)
.get()
.then((value) async {
return await value.exists ? _result = true : _result = false;
}
);
return _result;
await is meant to interrupt the process flow until the async method has finished. then however does not interrupt the process flow (meaning the next instructions will be executed) but enables you to run code when the async method is finished.
you can write your code like this-
Future <bool> checkVendorStatus (buyerId) async {
var _result;
var vendorDocRef = await buyersInfoColl.doc(buyerId)
.collection("vendorsCalled")
.doc(auth.currentUser!.uid)
.get();
vendorDocRef.exists ? _result = true : _result = false;
return _result;
}

Is there a way to get notified when a dart stream gets its first result?

I currently have an async function that does the following:
Initializes the stream
Call stream.listen() and provide a function to listen to the stream.
await for the stream to get its first result.
The following is some pseudo code of my function:
Future<void> initStream() async {
// initialize stream
var stream = getStream();
// listen
stream.listen((result) {
// do some stuff here
});
// await until first result
await stream.first; // gives warning
}
Unfortunately it seems that calling stream.first counts as listening to the stream, and streams are not allowed to be listened by multiple...listeners?
I tried a different approach by using await Future.doWhile()
Something like the following:
bool gotFirstResult = false;
Future<void> initStream() async {
var stream = getStream();
stream.listen((result) {
// do some stuff here
gotFirstResult = true;
});
await Future.doWhile(() => !gotFirstResult);
}
This didn't work for me, and I still don't know why. Future.doWhile() was successfully called, but then the function provided to stream.listen() was never called in this case.
Is there a way to wait for the first result of a stream?
(I'm sorry if I didn't describe my question well enough. I'll definitely add other details if needed.)
Thanks in advance!
One way is converting your stream to broadcast one:
var stream = getStream().asBroadcastStream();
stream.listen((result) {
// do some stuff here
});
await stream.first;
Another way, without creating new stream, is to use Completer. It allows you to return a Future which you can complete (send value) later. Caller will be able to await this Future as usual.
Simple example:
Future<int> getValueAsync() {
var completer = Completer<int>();
Future.delayed(Duration(seconds: 1))
.then((_) {
completer.complete(42);
});
return completer.future;
}
is equivalent of
Future<int> getValueAsync() async {
await Future.delayed(Duration(seconds: 1));
return 42;
}
In your case:
Future<void> initStream() {
var stream = getStream();
var firstValueReceived = Completer<void>();
stream.listen((val) {
if (!firstValueReceived.isCompleted) {
firstValueReceived.complete();
}
// do some stuff here
});
return firstValueReceived.future;
}