Flutter setstate() doesn't work with sleep() - flutter

I have a simple problem but I'm new to Flutter. I have to change the colors of 3 buttons, then wait 2 seconds and call another function.
This is the Code:
setState((){
clr[0]= 0==gst ? Colors.green:Colors.red;
clr[1]= 1==gst ? Colors.green:Colors.red;
clr[2]= 2==gst ? Colors.green:Colors.red;
});
sleep(const Duration(seconds:2));
cardKey.currentState.toggleCard(); // second function
The problem is that this code waits 2 seconds then change the colors and call the second function...
I tried also with the sleep() inside the setstate()

You should be using Future.delayed to solve your issues as sleep is not recommended. Make sure to define your function as async. However, if you don't want to mark as async (or the function doesn't work with async), you can use this:
Future.delayed(Duration(seconds: 2)).then((_) {
cardKey.currentState.toggleCard(); // second function
});
For more reading, I recommend this.

Related

What is the purpose of awaiting the empty Future in this animation code?

I was going though GSkinner's flutter_vignattes codebase, in one of the functions there was an empty await for a Future
Future<void> _reset() async {
// Wait until next event loop to advance animation and call setState or flutter will yell at you
await Future<void>.value();
_controller.forward(from: 1.0 - _percentage * 0.83);
if (_isLoading) {
setState(() {
_model = BasketballGameModel.randomize();
});
}
_isLoading = false;
}
I understand how promises are sent to micro-task queue in JS (assuming same happens in Dart), but not quite able to understand the reason provided in the comment here i.e.,
// Wait until next event loop to advance animation and call setState or flutter will yell at you
Really appreciate if someone can provide a deeper insight into this. This is the particular line in codebase i am referring to.
https://github.com/gskinnerTeam/flutter_vignettes/blob/0ccc72c5b87b5ab6ba2dee9eff76f48ce2fadec8/vignettes/basketball_ptr/lib/demo.dart#L149
Future<void> function() {}
Defines an asynchronous function that ultimately returns nothing but can notify callers when it eventually completes. Also see: What's the difference between returning void vs returning Future?
Or You can learn from this https://github.com/dart-lang/sdk/issues/33415

SetState in ternary operator

I want to change the color of the text displaying the scheduled time, if the scheduled time is past the current time then it should change to red and if its in the future then it wont change. This works properly but it only changes state when I click on another button. I am using a ternary operator like this:
color: (run(todoController.todos[index].date,
todoController.todos[index].time)
.compareTo(tz.TZDateTime.now(tz.local))>0)
? Theme.of(context).hintColor
: Colors.redAccent,
How do you add setState in a ternary operator? Thanks
I think the neater solution would be moving the logic of the comparison of time in a separate function which will be called each 2/5 second depending on your app. In that function, you can change the state and make sure to use color depending on the state.
The code for checking the future time each minute
var cron = new Cron();
cron.schedule(new Schedule.parse('*/3 * * * *'), () async {
if(run(todoController.todos[index].date,
todoController.todos[index].time)
.compareTo(tz.TZDateTime.now(tz.local))>0){
setState(() {
isFutureTime = true;
});
}
});
I have used corn. CORN documentation
Your Widget color code would look like this.
isFutureTime == false? Theme.of(context).hintColor : Colors.redAccent,
isFutureTime is the state boolean variable. You can follow this StackOverflow answer on changing the state periodically.

flutter rxdart Observable - how to unsubscribe

Here is my use case:
I have a following stream set:
PublishSubject<RewardedVideoAdEvent> _outVideoAdController =
PublishSubject<RewardedVideoAdEvent>();
StreamSink<RewardedVideoAdEvent> get _videoAdEvents => _outVideoAdController.sink;
Observable<RewardedVideoAdEvent> get outVideoAdEvents => _outVideoAdController.stream;
Now, I want to listen to outVideoAdEvents, hence I add this to my StatefullWidget initState method:
...
if (mounted) {
final AdMobBloc adMob =
BlocProvider.of<AppProvider>(context).application.adMobBloc;
adMob.outVideoAdEvents.listen((RewardedVideoAdEvent event) {
if (event == RewardedVideoAdEvent.rewarded){
// do something meaningfull
}
});
...
So far, so good.
The issue I have got - when if I open another widget and then come back to this one, initState executes again and hence, I have added another listener to the same stream. Then, the next time I issue an event into _outVideoAdController.sink, the callback will be executed twice.
Unfortunately, unlike initState, dispose does not execute each time I load another page, so I cannot figure out how to handle the above case.
Please note, the app uses rxdart: ^0.20.0
Any hints will be greatly appreciated!
you can unsubscribe the observable by this method:
_outVideoAdController?.close()
In case, someone else came across the same issue, these are the steps:
Add a private variable inside the widget from type StreamSubscription<T>, where T is your event type. In my case it is RewardedVideoAdEvent, hence I added StreamSubscription<RewardedVideoAdEvent> _videoAdSubscription;.
Then, when subscribing to the stream, the listen will return the value from this type, so just take it: _videoAdSubscription = adMob.outVideoAdEvents.listen((RewardedVideoAdEvent event) {});
Finally, when you want to unsubscribe, just call _videoAdSubscription?.cancel();
That's all.

A workaround to using an async operation inside setState

I have an async operation, the result of which should rebuild a widget.
The setState() doc says that
The provided callback is immediately called synchronously. It must not
return a future (the callback cannot be async), since then it would be
unclear when the state was actually being set
So I had a simple workaround which meets my purpose but I am not sure if this is the right approach.
// awaiting for an async operation
distanceFromCenter = await Geolocator().distanceBetween(
selectedLocation.latitude,
selectedLocation.longitude,
ReferencePoint.latitude,
ReferencePoint.longitude);
setState(() {
// A colleague might not know why we're calling the setState() here
});
Any suggestions are welcome.

What is the point of adding a callback to setState and not just calling setState without one? [duplicate]

This question already has answers here:
Why does setState take a closure?
(2 answers)
Closed 4 years ago.
I am still a bit confused by the difference between these two
isBusy = false;
setState(() {
});
and
setState(() {
isBusy = true;
});
What is the difference between the two? I have read the API but unfortunately, I am still not clear on what difference does it make. I know setState calls the build method of the widget. The API states
Whenever you change the internal state of a State object, make the
change in a function that you pass to setState: setState(() { _myState
= newValue }); The provided callback is immediately called synchronously.
What exactly does this mean? can anyone give me a super simple example of when this would make a difference?
There's no difference between using setState callback or not actually.
What's the point then ?
This is made voluntarily to prevent mistakes in handling asynchronous data.
By using the callback, there's a mistake you cannot do:
function() async {
setState(() {});
myState = await future;
}
This causes a problem because if your future doesn't finish synchronously, build method will be called with an invalid state.
By using the callback you are forced to do the following:
function() async {
final value = await future;
setState(() {
myState = value;
});
}
This time, it doesn't cause problems because the future is awaited before the setState.
Can't I make an async callback and stil have the issue?
No.
Because setState method internally check that the callback does not return a future. And if it does, it will throw