For Flutter, is it better to wrap only actual changes with setState()? - flutter

Is there a difference between wrapping the entire function and only wrapping the actual changes with setState()?
wrapping the entire function
setState(() {
if (switchValueAdd) {
valueAdd.text = (int.parse(num1) + int.parse(num2)).toString();
} else {
valueAdd.text = '';
}
});
wrapping only actual changes
if (switchValueAdd) {
setState(() {
valueAdd.text = (int.parse(num1) + int.parse(num2)).toString();
});
} else {
setState(() {
valueAdd.text = '';
});
}
Is there any difference in terms of performance between the above two code snippets?
Any help will be appreciated!
Also, with if-else statements, does 'else' cost as much memory as 'if'?
In other words, is 'else' basically the same as 'if' but checking if otherwise is true?
Thank you for your effort and time in advance!

Functionally, putting non-asynchronous code within the setState or putting it outside will result in very close to the same result. What you should try to avoid is having a huge block of code within the setState as this could be hard to read.
If you look at the source code for the setState method, it is just calling the function and doing a few asserts, so if you are doing the same amount of calls to it, it shouldn't have any appreciable difference. Theoretically, it could be very slightly slower if you are accessing local variables from within the closure, as those variables (or at least the references to them) could need to be copied through the stack into the function, but the actual overhead that will result in would need someone with a more complete understanding of dart's compiler for various platforms to fully explore.
Realistically, in terms of actual real-world performance, it's very unlikely to matter. There are probably other optimizations in your code which would result in much larger performance variance than this.
Also, from the setState documentation:
Generally it is recommended that the setState method only be used to wrap the actual changes to the state, not any computation that might be associated with the change. For example, here a value used by the build function is incremented, and then the change is written to disk, but only the increment is wrapped in the setState:
Future<void> _incrementCounter() async {
setState(() {
_counter++;
});
Directory directory = await getApplicationDocumentsDirectory();
final String dirName = directory.path;
await File('$dir/counter.txt').writeAsString('$_counter');
}
I would focus more on legibility in this case - whichever version you find easier to read is probably the one that will serve you best.
I'd probably write it like this:
setState(() {
valueAdd.text =
switchValueAdd ? (int.parse(num1) + int.parse(num2)).toString() : '';
});

It is not difference. setState called only in one case (from if or else, not from both). But if you will have to write readable code, use first variant because it is more readable. And statements is not a high-performance operation.
You can also simplify thin:
valueAdd.text = switchValueAdd
? (int.parse(num1) + int.parse(num2)).toString()
: valueAdd.text = '';

You can refresh state after executing all code of function so, it will reduce refresh build many times and improve performance of app.
valueAdd.text = switchValueAdd
? (int.parse(num1) + int.parse(num2)).toString()
: valueAdd.text = '';
/* All your other code which is need to manage state */
setState((){}); // call after all code executed of function

Related

Is it okay to write code logic (if, for, while) inside your build method

I have some data that I need to show in the UI based on the date of the running time
so I'm doing something like this inside my build method after getting my data from the Consumer Widget
DateTime dateTomorrow =
DateTime.now().add(const Duration(days: 1));
String tomorrowDay =
constants.dayFormat.format(dateTomorrow);
isWhileTrue = true;
while (isWhileTrue) {
if (myMainProgram.isDayShown(tomorrowDay)) {
isWhileTrue = false;
} else {
dateTomorrow =
dateTomorrow.add(const Duration(days: 1));
tomorrowDay =
constants.dayFormat.format(dateTomorrow);
}
}
I felt that there is something wrong with my way of handling data and writing some code logic inside my build method but I couldn't think of any other way to do it.
So is it wrong to write things like this inside your main function? and if not what are my other options?
No Its not okay to do like this because when ever the build function would rebuild by setState the data will revert.
The best way is to calculate in initState funtion.
InitState is the first function that is called when the widget is build and it runs once.
void initState(){
super.initState();
..your logic
}

My useQuery hook is refetching everytime its called. I thought it is suppose to hand back the cache?

I'm a little confused here. I thought react-query, when using useQuery will hand back 'cache' n subsequent calls to the same "useQuery". But everytime I call it it, it refetches and makes the network call.
Is this the "proper way" to do this? I figured it would just auto hand me the "cache" versions. I tried extending staleTime and cacheTime, neither worked. Always made a network call. I also tried initialData with the cache there.. didn't work.
SO, I am doing the following, but seems dirty.
Here is the what I have for the hook:
export default function useProducts ({
queryKey="someDefaultKey", id
}){
const queryClient = useQueryClient();
return useQuery(
[queryKey, id],
async () => {
const cachedData = await queryClient.getQueryData([queryKey, id]);
if (cachedData) return cachedData;
return await products.getOne({ id })
}, {
enabled: !!id
}
);
}
This is initiated like so:
const { refetch, data } = useProducts(
{
id
}
}
);
I call "refetch" with an onclick in two diff locations.. I'd assume after I retrieve the data.. then subsequent clicks will hand back cache?
I’m afraid there are multiple misconceptions here:
react query operates on stale-while-revalidate, so it will give you data from the cache and then refetch in the background. You can customize this behavior by setting staleTime, which will tell the library how long the data can be considered fresh. No background updates will happen.
when you call refetch, it will refetch. It’s an imperative action. If you don’t want it, don’t call refetch.
you don’t need to manually read from the cache in the queryFn - the library will do that for you.

How can I asynchronously stream loaded objects from a list of futures in Dart

I have a list of objects that can be loaded by calling the function object.load().
This function returns a loaded version of the same object asynchronously.
I want call the load() funcion of all objects in a list at the same time and stream the loaded versions as soon as they finish loading.
The code below works but the program is loading one object at a time.
Sender:
Stream<ImageIconModel> streamLoadedIcons() async* {
for (var i = 0; i < imageIconModels.length; i++) {
yield await imageIconModels[i].load().then((loadedIconModel) {
return loadedIconModel;
});
}
}
Receiver:
await for (var loadedIcon in streamLoadedIcons()) {
final var result = doSomething(loadedIcon);
yield result;
}
The main problem is:
In the sender, if I await each load() call, it will do every step in the loop awaiting the load() to finish.
But if I remove the "await", I would be returning a future, not the loaded icon.
You need Stream.fromFutures.
final loadedIconModelStream = Stream.fromFutures([
for (final iconModel in imageIconModels) iconModel.load(),
]);
Both #hacker1024 an #pskink answers successfully answered my question!
But neither one worked as it supposed to and I think I discovered why.
I substituted the load() method for a Future.delayed(duration: random), and then the program worked as it intended to.
So what I think happened is that probably the lib I'm using to load the images (multi_image_picker: ^4.7.14) is accessing the phone files synchronously.
So even if I try to load every image at same time, it will do the task synchronously and return every image at the order I called them to load.
Thank you both for the answer!

Update a series of variable in SetState() efficiently (Dart-Flutter)

I can't find a simple way to update a series of variables in my Flutter project.
I first tried using Enums and functions to change the variables inside a setState((){}) call.
I have something like this:
void changeMode(Mode mode) {
if (mode == Mode.start) {
print('App is now in start mode');
mode = Mode.start;
bool1 = true;
bool2 = false;
bool3 = false;
color1 = kAColor1;
color2 = kAColor2;
} else if ...}
But nothing gets updated, I imagine it's due the fact that my function doesn't return anything.
If I hard code every single variable in setState((){}) it works fine, but it's absolutely inefficient and a mess to correct.
Maybe I should go with classes? Would I need to create a superclass containing all the subclasses to do something like this?
Every time that you call setState you UI will rebuild. You can use class or map to manipulate your data.
With class:
setState(() {
currentData = actualData.copyWith(bool1: false)
})
This way, you change only data that is different from currentData. On this example, I maintain all information from currentData and change only bool1 value.
Obs: copyWith is a factory that return the same type of
currentData.

Flutter: Best practice for nested async HTTP calls

I'm trying to load the result of two Http requests into a list-item (the second request is a get detail about the first request result item).
The first request gets a list of cryptocurrencies.
The second request collects more information about that currency.
The reason why I want to accomplish this in two separate requests is,
the data is on different sources.
after I got the list of currencies I want to show a process circle, which means "stay calm, details are loading"
What I already tried is ;
One stateful-widget with two methods ( both are calling setState) it was catastrophic. Endless loop.
One base stateful-widget and a second child stateful-widget in the build method. Worked but whenever I scroll up or down, the list re-renders its items, and the child stateful-widget makes the same getPrice HTTP request again and again.
One inherited-widget and one stateless-widget. Had to use the context for both of the HTTP request methods. It caused endless loops again.
Currently, a part of my Brain yells "Redux is the only way to handle this in a proper way"
Do you have any suggestions how to handle this?
Thank you in advance.
void getOwnedCoins() async {
CoinsList cList = await ResourceCoins().list();
setState(() {
list = cList;
items = cList.result.length;
});
getCoinSummary(cList);
}
void getCoinSummary(CoinsList coins) {
coins.result.forEach((Coin coin) async {
CoinSummary cs = await ResourceCoinSummary().summary(market: {'market': 'btc-' + coin.currency});
coin.summary = cs;
setState(() {
list = coins;
});
});
}
That's how I solved it. My mistake was to call the getCoinSummary in the ListView.builder. However, I feel not comfortable with that setState in getCoinSummary. Every time I get details from server I re-render the whole list. Is it how it should be?