Update a series of variable in SetState() efficiently (Dart-Flutter) - 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.

Related

How to create a method inside a provider class

I want to be clear and precise. I have a database with 260 variables, I have a data model class and a SQLiteHelper class. I'm using a provider and I have all the CRUD inside.
The problem comes because I have scrolleable page in which I want to be able to change all variables. Around 240 variables are int? and each one will have a button which do the same, convert it to zero if it is null or add 1 if it is an integer. I'm not using a normal callback because in that scrolleable page I use different reusable buttons and I want to know the value of the variable in child and parent widget. In the reusable buttons for change color and text, and in the parent widget(scrolleable page) to save them in SQlite at the end of the page with a save button.
This is my provider class
class DBProvider with ChangeNotifier {
final SQLiteHelper0 _db = SQLiteHelper0();
List<DB> _items = [];
Future<void> loadDB() async {
List<Map<String, dynamic>> data = await _db.charDB;
_items = data.map((DB) {
return DB(
id: charDB["id"],
name: charDB["name"],...// the rest of the CRUD
I'm trying something like that
dynamic increment(index){
if(_items[index] != int?){
return _items[index];
} else if (_items[index]! == null){
return _items[index]== 0;
}else { return _items[index] + 1;}
}
an then in the scrolleable page make another function like that
late DBProvider _dBProvier;
void _increment(index){setState(() {
_dBProvider.increment(index);
});}
I am having different problems, at times I think that nothing makes sense. But if it is a totally dumb way, please give me some direction to keep trying. This question is related with other question where I focused the problem in a different and wrong way Why this function is null?
Thanks.

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

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

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
}

Dynamic choice of variable by a function in Dart (Flutter)

I am building my first app with Flutter, here's the problem I stuck with:
I have several tabs with radio buttons, the tabs are generated dynamically. I am using two functions to handle the changes in those radio buttons. Each function is used in every tab.
My problem is that I don't know how to assign the value of the radio buttons to different variables.
I precreated the variables, but how do I access them from my functions? I tried do do it this way:
var bedsRoom1, bedsRoom2... bedsRoom7; //7 variables which is maximum of what I need
I use the functions as a callback inside the state of the stateful widget. Inside the function I tried to do:
void _handleRadioValueChange1(int value, int counter) {
setState(() {
_radioValue1 = value;
bedsRoom$counter = value;
});
}
I get error messages about an undefined name of a variable. Please explain how to do it right.
Instead of declaring multiple variables, you can declare a list of variables like this -
List bedRooms = new List(7);
Then you can access the variables like this -
void _handleRadioValueChange1(int value, int counter) {
setState(() {
_radioValue1 = value;
bedsRooms[counter] = value;
});
}
You can read more about List in dart from here -
Lists in dart

MVVM viewmodel property triggering update

I started implementing MVVM for one of my Silverlight applications.
(I'm not using any toolkit).
My page contains a section with two combo boxes. Selecting an item in one of these combos triggers a search that updates a grid visible below the combos.
Each combo's selected item is bound to a property in my view model. The setter of these properties raise the INotifyPropertyChanged property change and updates the data bound to the grid automatically.
Everything was fine until I needed to add a reset button which purpose is to reset the search parameters i.e.: each combo box should not indicate any item and the grid should be empty.
If the reset function in the viewmodel updates the backing fields, the UI won't reflect the changes as RaisePropertyChanged will not be called.
If the reset function in the viewmodel updates the properties, the UI will reflect the changes but the grid will be updated twice: when reseting the first property to null and also for the second
Any help appreciated
/// <summary>Selected user.</summary>
public User SelectedUser
{
get { return _selectedUser; }
set
{
_selectedUser = value;
RaisePropertyChanged("SelectedUser");
UpdateProducts();
}
}
/// <summary>Selected product category.</summary>
public ProductCategory SelectedProductCategory
{
get { return _selectedProductCategory; }
set
{
_selectedProductCategory = value;
RaisePropertyChanged("SelectedProductCategory");
UpdateProducts();
}
}
// Reset option 1
public void Reset()
{
_selectedUser = null;
_selectedProductCategory = null;
_products = null;
}
// Reset option 2
public void Reset()
{
SelectedUser = null;
SelectedProductCategory = null;
// No need to update Products which has already been updated twice...
}
This is something that really urks me in many frameworks, WPF included. What you need is some concept of delaying the response to change notifications so that the user never sees intermediate states. However, you can't change the way WPF responds to your notifications, so the best you can do is to delay your notifications until "after the dust has settled". In your case, you will want to change both of the backing fields before any notifications are sent. Your reset method can encode this idea as follows:
public void Reset()
{
_selectedUser = null;
_selectedProductCategory = null;
_products = null;
RaisePropertyChanged("SelectedUser");
RaisePropertyChanged("SelectedProductCategory");
}
In my opinion, the way WPF synchronously updates the display in response to notifications of change is just plain wrong. Their DependencyProperty system gives them an opportunity to merely mark dependencies as dirty and perform recalculation at a later time.
I use the idea of marking as dirty and asynchronous recalculation as a general solution to the problem you've noted in this question and these days I could not imagine programming without it. It's a shame that more frameworks do not work this way.
You can raise a single PropertyChanged event for all properties after you updated the backing fields:
RaisePropertyChanged(String.Empty);
If you use the backing Fields you would have to call
RaisePropertyChanged("SelectedUser");
RaisePropertyChanged("SelectedProductCategory");
in the Reset() method.