How to make model fields observable in Flutter - flutter

I have a model called category , which has two fields, User has a list of categories and what to update the category name, for State Management getx is used
class Category {
String? categoryName;
bool status;
Category(this.categoryName,this.status);
}
I have a observable list called catList which is used in List widget
var catList = <Category>[].obs;
when I update the category field it doest not update
catList[index].categoryName = categoryChangedName.value;
but If I update the item in object and then assign the object to catList then It changed
catList[index] = Category(categoryChangedName.value, catList[index].status );
My question is how to make model fields observable, if we have more fields changes then this is not proper way.

As of GetX documentation you need to update values using method and call update(); method inside custom object !
Ex:
class Controller extends GetxController {
int counter = 0;
void increment() {
counter++;
update(); // look here!
}
}
Your use case might be like....
class Category {
String? categoryName;
bool status;
Category(this.categoryName,this.status);
void updateCategoryName(name){
this.categoryName = name;
update();
}
}
//Use like..
catList[index].updateCaetgoryName = categoryChangedName.value;

Related

How to listen for changes to an object's property inside a View Model using Provider in flutter?

Let's say I have the following view model class
class Foo extends ChangeNotifier{
//Object variable to which changes will be made
Object _fooObject = Object();
// Getter and Setter of Object
Object get fooObject => _fooObject;
set fooObject(Object value) {
_fooObject = value;
notifyListeners();
}
}
And let's say this is my Object Class
class Object{
int? property1;
String? property2;
Object(this.property1,this.property2);
}
Now If I assign a new Object() to fooObject the changes will easily reflect because I have called notifyListeners() in the setter but what If I change a single property in the fooObject let's say I do
Provider.of<Foo>(context).fooObject.property1 = 2;
How do I exactly detect these changes in realtime? do I have to make a separate getter setter for each property? What's the best solution to call notifyListeners() on property1's change?
you can determine using the identical(a , b) function.
set fooObject(Object value) {
if(identical(_fooObject , value)){
_fooObject = value;
notifyListeners();
}
}

Duplicate Array list value automatically changed in Flutter

I'm very surprised after this issue. First, inform all things I have used in my project.
I have used Getx in my project. I have called the API using the Getx controller file.
Below code used in getx controller file. "PLTimeSlotModel" is model and. it has two params (name, isselect).
var futureTimeSlot_PL_C_D = Future.value(<PLTimeSlotModel>[]).obs;
callTimeSlotAPI() async {
futureTimeSlot_PL_C_D.value = FetchTimeSlotList();
}
Future<List<PLTimeSlotModel>> FetchTimeSlotList() async {
// Fetching data with API calling
}
Screen A:
List<PLTimeSlotModel> listA = [];
List<PLTimeSlotModel> listB = [];
#override
void initState() {
super.initState();
_plController.callTimeSlotAPI();
}
Another method is to create two lists using the future list:
List<PLTimeSlotModel> temp1 = await _plController.futureTimeSlot_PL_C_D.value;
temp1.forEach((element) {
listA.add(element);
listB.add(element);
});
onclick:
for(int i =0;i<listA.length;i++){
listA[i].isselect = false;
print(listA[i].isselect);
print(listB[i].isselect);
}
Now the issue is I have changed/updated the only "listA" value, So why automatically set the same value to "listB"? The two list is based on the one list.
A List in Dart contains references objects, not the objects themselves. So what you are doing is copying references to objects into two lists. But since they are pointing at the same objects, you will see any modification in one list also happen in the other list. You need to copy each PLTimeSlotModel object and put the copy into your new list.
One way to copy objects is to create an constructor which takes an object of the same type and creates a new object based on this first object. So something like this:
class Person {
String name;
Person(this.name);
Person.fromPerson(Person other) : this(other.name);
#override
String toString() => 'Person($name)';
}
void main() {
final persons = [
Person('Adam'),
Person('Bob'),
];
// Make new list based on persons
final otherPersonList = [
...persons.map((person) => Person.fromPerson(person))
];
otherPersonList[0].name = 'Carl';
print(persons); // [Person(Adam), Person(Bob)]
print(otherPersonList); // [Person(Carl), Person(Bob)]
}

Flutter: In RiverPod , how to alter state model's variables directly?

I am currently learning River Pod and also new to flutter.
When setting new state in StateNotifer , I need to create a new model and replace the state
But directly changing is not working
class CounterModel {
CounterModel(this.count, this.age);
int count;
int age;
}
class CounterNotifier extends StateNotifier<CounterModel> {
CounterNotifier() : super(_initialValue);
static CounterModel _initialValue = CounterModel(0,18);
void increment() {
// state.count = state.count + 1; // not working, but need like this !
state = CounterModel(state.count + 1, state.age); // working
}
}
In the above code , when I trying to change the count variable directly like, state.count = state.count + 1 , nothing changed
But when reinitialising the state by creating a new model like state = CounterModel(state.count + 1, state.age)
Its seems to be state model variables are immutable and needs to be recreated on every alteration !
My question is , what if the CounterModel have 50 variables , then I have to do something like
state = CounterModel (var1,var2,......,var49,var50) ;
So , is it possible to directly change the variables like
state.var1 = new_value1;
state.var2 = new_value2;
....
state.var50 = new_value50;
You have to always reassign the state in StateNotifier for Consumers to see the changes hence, state.var1 = new_value1; can't work with StateNotifier
If You're very keen about that syntax, use ChangeNotifier since it allows you change individual properties of the class but you must call notifyListeners
Like so:
class CounterNotifier extends StateNotifier {
static CounterModel value = CounterModel(0,18);
void increment() {
value.count = value.count + 1;
notifyListeners();
}
}
If You want to stick with StateNotifier and don't want to write boilerplate code, create a copyWith method on the model.
Like so:
class CounterModel {
CounterModel(this.count, this.age);
int count;
int age;
CounterModel copyWith({int? count, int? age}){
return CounterModel(
count ?? this.count,
age ?? this.age
);
}
}
Then you can keep reassigning with it like so:
class CounterNotifier extends StateNotifier<CounterModel> {
CounterNotifier() : super(_initialValue);
static CounterModel _initialValue = CounterModel(0,18);
void increment() {
state = state.copyWith(count: state.count + 1);
}
}

flutter add method was called on null using provider

Hello I want to add some value to my list. I have already googled for solutions but I can't see any other solutions besides initializing the list which I did.
When I try to add an item to my AutomaticDateList class I get the error:
The method 'add' was called on null.
Receiver: null
Tried calling: add(Instance of 'AutomaticDate')
Here is the class with the list in it.
class AutomaticDateList with ChangeNotifier {
List<AutomaticDate> items = []; // here I inialize
AutomaticDateList({this.items});
void addToList(AutomaticDate automaticDate) {
items.add(automaticDate);
notifyListeners();
}
List<AutomaticDate> get getItems => items;
}
This is the item I want to add to the list.
class AutomaticDate with ChangeNotifier {
String date;
String enterDate;
String leaveDate;
String place;
AutomaticDate({this.date, this.enterDate, this.leaveDate, this.place});
Here I call the method using the provider inside a page widget
void onGeofenceStatusChanged(Geofence geofence, GeofenceRadius geofenceRadius,
GeofenceStatus geofenceStatus) {
geofenceController.sink.add(geofence);
AutomaticDate automaticDateData = AutomaticDate();
automaticDateData.place = geofence.id;
automaticDateData.date = DateFormat("dd-mm-yyyy").format(DateTime.now());
if (geofenceStatus == GeofenceStatus.ENTER) {
widget.enterDate = DateFormat("HH:mm:ss").format(DateTime.now());
} else {
automaticDateData.leaveDate =
DateFormat("HH:mm:ss").format(DateTime.now());
automaticDateData.enterDate = widget.enterDate;
widget.list.add(automaticDateData);
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
AutomaticDateList automaticDateList =
Provider.of<AutomaticDateList>(context, listen: false);
automaticDateList.items.add(automaticDateData); // Here I add the data and get error "add was called on null"
print(automaticDateList.getItems);
});
}
}
The problem is in the initialization:
List<AutomaticDate> items = []; // here I inialize
AutomaticDateList({this.items});
You set a default value, but you are using the "auto-assign" sintax in the constructor, saying that items passing in the parameters are going to be assigned in the items property of the class.
You are instantiating the class using this code:
AutomaticDate automaticDateData = AutomaticDate();
So, you are passing "null" implicitly as parameter, then items [] got replaced with null value.
Just change the code to:
List<AutomaticDate> item;
AutomaticDateList({this.items = []}); // Default value

How to observe ObservableList item properties changes

I'm building a shopping app in Flutter using MVC pattern and mobx for app state management.
At the moment, I have a mobx store for cart items and one store for the cart controller.
The cart controller has a ObservableList of cart items and problem is that I don't know if there's a way of observing changes on cart items.
For instance, I'd like to observe cartItem.title or cartItem.total.
Is there a way to track this with ObservableList?
And whats is .observe() method the observable list has? (Think the documentation wasn't clear for me)
UPDATE: SAMPLE CODE
As I said I have to mobx sotres, one for the cart item and for the cart itself.
In the cart item store:
import 'package:mobx/mobx.dart';
part 'cart-item.model.g.dart';
class CartItemModel = _CartItemModel with _$CartItemModel;
abstract class _CartItemModel with Store {
int id;
String title;
String price;
String description;
#observable
int _quantity = 0;
#observable
double _total = 0;
_CartItemModel({
this.id,
this.title,
this.price,
this.description,
}) {
reaction(
(_) => _quantity,
(quantity) {
getTotal();
},
);
}
getItemQuantity() => _quantity.toString(); // Return item quantity
#action
increase() {
// Increase item quantity
if (_quantity <= 99) {
_quantity++;
}
}
#action
decrease() {
// Decrease item quantity
if (_quantity > 0) {
_quantity--;
}
}
#action
getTotal() {
// Return total price by item quantity
_total = double.parse(price) * _quantity;
return _total.toString();
}
}
And then in the cart controller:
import 'package:faccioo_user_app/models/cart-item.model.dart';
import 'package:mobx/mobx.dart';
part 'cart.controller.g.dart';
class CartController = _CartController with _$CartController;
abstract class _CartController with Store {
#observable
ObservableList<CartItemModel> cartItems = ObservableList<CartItemModel>();
#action
addItem(CartItemModel item) {
cartItems.insert(0, (item));
item.increase();
}
#action
removeItem(CartItemModel item) {
cartItems.removeWhere((cartItem) => cartItem.id == item.id);
getTotal();
}
#action
getSubtotal() {
cartItems.forEach((item) {
subtotal = subtotal + double.parse(item.getTotal());
});
return subtotal.toString();
}
#action
getTotal() {
total = (subtotal + shippingFee + serviceFee + change) - discount;
return total.toString();
}
}
The view is not being notified by the changes in cartItem.total, for example?. How do I observe changes in cartItemModel.total from ObservableLis?
To be more clear I got this print in which we can see that cart item quantity and total increase, therefore CartItemModel reactivity is working fine, but the cart controller can't track those changes from ObservableList, then the controller is not updating the view.
I'd really appreciate links and references from where I can learn more about mobx with Flutter and observable lists.
Cart view with cart item
this is an old question but I will share the hints for the solution and how to improve it.
You don't need to create a store for the model, just wrap the properties with #observable, #computed, etc.
Actions are meant to write to the store and update the store properties. If you want to read properties, just write a simple get property without the #action annotation, or if you want to read a property based on other #observable properties, use the get with #computed.
The store should be only the CartController in which you should have a #computed property that will change based on other properties changes, e.g. for the CartController store:
The CartItemModel should be transformed into a simple data class with getters and setters and is the store that will manage its state.
I didn't check in terms of types .. etc.., the getters are missing the return types, only the setters that don't need return type.
#computed
double get total() => subtotal + shippingFee + serviceFee + change - discount;
#computed
double get subtotal() {
double subTotalAggregator = 0;
cartItems.forEach((item) {
subTotalAggregator += double.parse(item.getTotal());
});
return subTotalAggregator;
}