I have a shopping cart project using provider. When changing variant(array of object) the already pushed value inside the cart also changes. How do I prevent the value from changing when it is already inserted.
Adding items to cart
addProduct() async{
final productDetailsProvider =Provider.of<ProductDetailsProvider>(context,listen: false);
List<VariantDetails>? variants = productDetailsProvider.variants;
List<AddOnDetails>? extras = productDetailsProvider.extras;
double total = productDetailsProvider.total;
CartData cartItem = CartData(
addOnList: extras,
variantList: variants,
total:total);
final cart =Provider.of<CartProvider>(context,listen: false);
cart.addToCart(context, cartItem);
}
Provider class - The cart array variantList still changes when clicking button again. Should not change for the already pushed/added values. Total does not change only variantList and extras
class CartProvider extends ChangeNotifier {
List<CartData>? cart = [];
addToCart(context,CartData cartItem) {
if (cart!.isEmpty) {
cart?.add(cartItem);
}
print('${cart?[0]?.variantList}');//variantList changes value even when it's already pushed to array
}
Provider class changing variant for reference
class ProductDetailsProvider extends ChangeNotifier {
List<VariantDetails>? variants = [];
addVariant(VariantDetails? item) {
var exist =
variants?.indexWhere((element) => element.typeID == item?.typeID);
if (exist != -1) {
variants?[exist!] = item!;
} else {
variants?.insert(0, item!);
}
}
}
Related
I have the below user defined objects -
class PhoneBookContact {
String? phoneBookContact;
List<ContactNumber>? contactNumbers;
PhoneBookContact(this.phoneBookContact, this.contactNumbers);
}
class ContactNumber {
String phone;
bool availableOnBol;
ContactNumber(this.phone, this.availableOnBol);
}
In my main method I am creating a list for the ContactNumber class and then later adding that ContactNumber list to the contactNumbers property of PhoneBookContact list. This is all done inside of a for loop.
The issue I am having is when I am clearing the contactNumbers list after adding those items to the contactNumbers property of the PhoneBookContact list, I see those cleared from the PhoneBookContact list as well, which I find weird, or maybe I am not thinking it the right way.
List<PhoneBookContact> phoneBookContacts = [];
List<ContactNumber> contactNumbers = [];
for (var contact in contacts) {
contactNumbers.clear();
if (contact.phones.isNotEmpty) {
for (var phone in contact.phones) {
if (true) {
contactNumbers.add(ContactNumber(phone.number, true));
} else {
contactNumbers.add(ContactNumber(phone.number, false));
}
}
}
phoneBookContacts
.add(PhoneBookContact(contact.displayName, contactNumbers));
}
Your PhoneBookContact constructor does not make a copy of the list. It takes a reference to the list and remembers it.
Since you clear the list every loop but reuse the same list instance, all your PhoneBookContacts will have the same list.
Lets clean up your method a little:
List<PhoneBookContact> phoneBookContacts = [];
for (var contact in contacts) {
List<ContactNumber> contactNumbers = [];
for (var phone in contact.phones) {
final condition = true; // should be your complicated expression
contactNumbers.add(ContactNumber(phone.number, condition));
}
phoneBookContacts.add(PhoneBookContact(contact.displayName, contactNumbers));
}
Apart from removing some clutter, it makes sure that each loop, you instantiate a new list so that each PhoneBookContact has it's own list.
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)]
}
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;
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
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;
}