Flutter: what can I put in setState() method? - flutter

is there a meaningful difference between these?
setState(() {
_registerIndex++;
});
_registerScrollController.jumpTo(0);
and
setState(() {
_registerIndex++;
_registerScrollController.jumpTo(0);
});

There is no difference, as long as none of them awaits for a Future.
However, if you don't need to update the counter value in your UI, you also don't need setState in this case, because ScrollController.jumpTo() will do the job internally.

Related

Why does Flutter riverpod direct assignment does not work but methods do

Please check the two samples below.
The first sample does not rebuild the Widgets [Possibly 'listeners'
are not being 'notified']
The second sample works as
expected
To my understanding, i think all these two should work. Can someone brief me on the comprehension I'm lacking?
Thanks in advance.
Sample one (Does not rebuild) [ui changes do not take effect ]
onTap: (String? newValue) {
ref.watch(UserProvider).selectedMaritalStatusValue = newValue!;
UserModel().notifyAllListeners(); //triggers notifyListeners
},
Sample Two (Does rebuild)[working fine]
onTap: (String? newValue) {
ref.watch(UserProvider).setMaritalStatus(newValue!); // 'setMaritalStatus' has notifyListeners trigger within
},
Firstly, you should not use ref.watch inside any onTap callbacks. Use ref.read here instead. Read this for clarity on why this is the case.
Secondly, in your first code block where you write:
UserModel().notifyAllListeners();
UserModel() creates a new object altogether, and notifyAllListeners() is being called for this new object. This new object is not being watched inside the build method of this widget. This is why the first code block you posted fails to rebuild the widget.
Thirdly, as a best practice, methods like notifyListeners() and direct assignments of fields in any class should be done inside the class's code. Use your second code block as a reference in future. This is the correct and safest way.
You can use a private variable setter, then call notifyListeners in the setter after updating the variable like so:
class UserProvider extends ChangeNotifierProvider{
String? _selectedMaritalStatusValue;
String? get selectedMaritalStatusValue => _selectedMaritalStatusValue;
set selectedMaritalStatusValue(String? newValue){
_selectedMaritalStatusValue = newValue;
notifyListeners();
}
}
Now, this should work:
ref.watch(UserProvider).selectedMaritalStatusValue = newValue!;

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

How to perform setState() while listening to a Stream

I hope you are doing well. I was looking for a way where I can perform setState within a Stream. Basically what I am doing here is, I am getting the data from the collection, assigning it to a List variable called currentDocuments and I am displaying it in a ListView.builder(). I did not use StreamBuilder because, the documents can be deleted, but have to be deleted locally and not from firestore.
The problem is when I add a new document to 'mycollection' collection, I get this message:
This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build). This error can occur when code calls setState() from a timer or an animation callback. The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback. Another solution is to check the "mounted" property of this object before calling setState() to ensure the object is still in the tree. This error might indicate a memory leak if setState() is being called because another object is retaining a reference to this State object after it has been removed from the tree. To avoid memory leaks, consider breaking the reference to this object during dispose().
I have tried a lot of ways using 'mounted' field, but still, I get the message mentioned above. Any help regarding this issue would be greatly appreciated. Thank you in advance.
Code:
_subscription = Firestore.instance.collection('mycollection').snapshots().listen((QuerySnapshot querySnapshot) {
for(var i in querySnapshot.documentChanges){
if(i.type == DocumentChangeType.added){
if(!mounted){
setState((){});
}
setState(() {
currentDocuments = querySnapshot.documents;
});
}
}
});
_subscription = Firestore.instance.collection('mycollection').snapshots().listen((QuerySnapshot querySnapshot) {
for(var i in querySnapshot.documentChanges){
if(i.type == DocumentChangeType.added){
if(mounted){
setState(() {
currentDocuments = querySnapshot.documents;
});
}
}
}
});
here mounted will be true if the widget exists in the tree, but in your code there are two setState calls which will fire either the mounted is true or not!!, use/call setState only when mounted is true,

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