I have 2 arrays cart and cartCopy, inside that array I have an object. Data is inserted in cart and cartCopy. When updating the variant or extras in cart I first create a separate list for them variantList=[] and extrasList=[], now when updating variantList/extrasList the values in cart and cartCopy also changes even though I did not update them.
{"product_name:"",variants:[],extras:[]}
Provider class changing variant for reference and Setting Product to Edit. Whenever I update the variant, cart and cartCopy variant also changes. How to I separate the values in cart?
class ProductDetailsProvider extends ChangeNotifier {
List<VariantDetails>? variants = [];
List<VariantDetails>? selectedVariants = [];
List<AddOnDetails>? selectedExtras = [];
addVariant(VariantDetails? item) {
var exist =
variants?.indexWhere((element) => element.typeID == item?.typeID);
if (exist != -1) {
variants?[exist!] = item!;
} else {
variants?.insert(0, item!);
}
}
setProduct(CartData item) async {
variants = item.variant;
selectedExtras = item.extras;
selectedVariants = item.variant;
notifyListeners();
}
}
Add to Cart Class
class CartProvider extends ChangeNotifier {
List<CartData>? cart = [];
List<CartData>? cartCopy = [];
addToCart(context, CartData cartItem, isUpdate) async {
cart?[exist!] = cartItem;
cartCopy?[exist!] = cartItem;
}}
You can use the following methods to encode and decode the list of CartData
static String encode(List<CartData> cart) => json.encode(cart
.map<Map<String, dynamic>>((value) => CartData.toMap(value))
.toList(),
);
static List<CartData> decode(String cart) {
return cart.isNotEmpty
? (json.decode(cart) as List<dynamic>)
.map<CartData>((item) => CartData.fromJSON(item))
.toList()
: [];
And in CartData class you should have something like this for the object
CartData.fromJSON(Map<String, dynamic> jsonMap)
: id = jsonMap['id'],
name = jsonMap['name'],
price = jsonMap['price'],
tax = jsonMap['tax'];
static Map<String, dynamic> toMap(CartData cartItem) => {
'id': cartItem.id,
'name': cartItem.name,
'price': cartItem.price,
'tax': cartItem.tax
};
Those methods would allow you to do this:
cartCopy = CartData.decode(CartData.encode(cart));
Related
I'm making a midel to upload product image,price and name to firebase then i face this error (The argument type 'ProductModel?' can't be assigned to the parameter type 'ProductModel'.)
class ProductProvider with ChangeNotifier {
List<ProductModel> pizzaProductList = [];
ProductModel? productModel;
fatchPizzaproductData() async {
// List<ProductModel> newList = [];
QuerySnapshot value =
await FirebaseFirestore.instance.collection("PizzaProducts").get();
pizzaProductList = value.docs.map((element) {
return ProductModel(
productImage: element.get("productImage"),
productName: element.get("productName"),
productPrice: element.get("productPrice"),
);
}).toList();
}
get getPizzaproductDataList {
return pizzaProductList;
}
}
The problem is that productModel is a nullable type, whereas pizzaProduct is a List of non-nullable ProductModels.
Instead of storing a property productModel on your class, consider mapping directly from value.docs to pizzaProduct, and removing the intermediate step of storing the model in productModel:
class ProductProvider with ChangeNotifier {
List<ProductModel> pizzaProduct = [];
Future<void> fetchPizzaProductData() async {
QuerySnapshot value =
await FirebaseFirestore.instance.collection("PizzaProducts").get();
pizzaProduct = value.docs.map((element) {
return ProductModel(
productImage: element.get("productImage"),
productName: element.get("productName"),
productPrice: element.get("productPrice"),
);
}).toList();
// Since this is a ChangeNotifier, I'm assuming you might want to
// notify listeners when `pizzaProduct` changes. Disregard this line
// if that's not the case.
notifyListeners();
}
}
I have two provider classes where as a property there is an instance of a Combo object.
My problem is when I modify value properties of Combo object of provider one, the instance of provider two is modified as well.
This is a problem for me because it makes me impossible to have two different instances even when they are created on different classes.
#immutable class Combo
{
Combo(
{
this.idCombo = 0,
this.idThirdCombo = 0,
this.name = '',
this.urlImage = '',
final List<int>? recipy,
final List<Product>? selectedRecipy,
final List<OptionalRecepy>? optionalRecepy,
final SwitStoreProductType? type,
}) :
this.recipy = recipy ?? [],
this.selectedRecipy = selectedRecipy ?? [],
this.optionalRecepy = optionalRecepy ?? [],
this.type = type ?? SwitStoreProductType.none;
final int idCombo;
final int idThirdCombo;
final String name;
final String urlImage;
final List<int> recipy;
final List<Product> selectedRecipy;
final List<OptionalRecepy> optionalRecepy;
final SwitStoreProductType type;
}
//Provider One
class ProductDetailsBSProvider extends ChangeNotifier
{
Combo? _currentCombo;
void modifyCombo(Product product, int index)
{
if(index != this._currentOptionIndex)
{
if(this._currentCombo!.selectedRecipy.length > 1)
{
int previousIndex = (this._currentCombo!.selectedRecipy.length - 1);
this._currentCombo!.selectedRecipy.removeAt(previousIndex);
this._currentCombo!.selectedRecipy.insert(previousIndex, product);
this._currentOptionIndex = index;
}
else
{
this._currentCombo!.selectedRecipy.add(product);
this._currentOptionIndex = index;
}
}
else
{
if(this._currentCombo!.selectedRecipy.length == 0)
{
this._currentCombo!.selectedRecipy.add(product);
}
else
{
this._currentCombo!.selectedRecipy.removeLast();
this._currentCombo!.selectedRecipy.add(product);
}
}
notifyListeners();
}
}
//Provider Two
class StoreProvider extends ChangeNotifier
{
Combo? _currentCombo;
}
If I print the _currentCombo properties value of Provider Two it will be exactly the same as Provider One
I have been working with my Store App, but this null safety is getting me pissed now. I have created a class but it gives me this error with later doesn't allow my app to work correctly
this is the the product.dart file:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:loja_virtual_nnananene/models/item_size.dart';
class Product extends ChangeNotifier {
Product.fromDocument(DocumentSnapshot document) {
id = document.documentID;
name = document['name'];
description = document['description'];
images = List<String>.from(document.data['images'] as List<dynamic>);
// ingore_for_file: Warning: Operand of null-aware operation
sizes = (document.data['sizes'] as List<dynamic> ?? [])
.map((s) => ItemSize.fromMap(s as Map<String, dynamic>))
.toList();
}
String id = "";
String name = "";
String description = "";
List<String> images = [];
List<ItemSize> sizes = [];
ItemSize _selectedSize;
ItemSize get selectedSize => _selectedSize;
set selectedSize(ItemSize value) {
_selectedSize = value;
notifyListeners();
}
}
I'm receiving an error at the Product.from...
This is the error:
Non-nullable instance field '_selectedSize' must be initialized.
Try adding an initializer expression, or add a field initializer in this constructor, or mark it 'late'.
This is my ItemSize class:
class ItemSize {
ItemSize.fromMap(Map<String, dynamic> map) {
name = map['name'] as String;
price = map['price'] as num;
stock = map['stock'] as int;
}
String name = "";
num price = 0;
int stock = 0;
bool get hasStock => stock > 0;
#override
String toString() {
return 'ItemSize{name: $name, price: $price, stock: $stock}';
}
}
Calling in main widget:
class SizeWidget extends StatelessWidget {
const SizeWidget(this.size);
final ItemSize size;
#override
Widget build(BuildContext context) {
final product = context.watch<Product>();
final selected = size == product.selectedSize;
Color color;
if (!size.hasStock)
color = Colors.red.withAlpha(50);
else if (selected)
color = ColorSelect.cprice;
the code tries to get the selected item the first time, and of course it will be null. The alternative I found was...
in ItemSize class, i create a construtor simple with all null -> ItemSize();
class ItemSize {
ItemSize.fromMap(Map<String, dynamic> map) {
name = map['name'] as String;
price = map['price'] as num;
stock = map['stock'] as int;
}
ItemSize();
String? name;
num? price;
int? stock;
bool get hasStock => stock! > 0;
#override
String toString() {
return 'ItemSize{name: $name, price: $price, stock: $stock}';
}
}
in the Product class do the get this way.
ItemSize get selectedSize {
if (_selectedSize != null)
return _selectedSize!;
else
return ItemSize();
}
I'm hoping someone can tell me how to load JSON in to a PageView. Each page in the PageView will contain a ListView which will use Card widgets to display each Job from the JSON.
The JOB_DATE will dictate which page the job is displayed on. So in the JSON below, the first 3 items are on one date and the next 2 items are on the following date. So page 1 should display the first 3 items and page 2 should display items 4 & 5.
JSON :
{
"rows":[
{ "JOBID":23, "JOB_DATE":1588809600000, "START_TIME":"07:30", "JOB_NAME":"Cleaner" },
{ "JOBID":24, "JOB_DATE":1588809600000, "START_TIME":"08:30", "JOB_NAME":"Manager" }
{ "JOBID":25, "JOB_DATE":1588809600000, "START_TIME":"12:30", "JOB_NAME":"Caretaker" }
{ "JOBID":26, "JOB_DATE":1588896000000, "START_TIME":"08:30", "JOB_NAME":"Manager" }
{ "JOBID":27, "JOB_DATE":1588896000000, "START_TIME":"13:30", "JOB_NAME":"Caretaker" }
]
}
How would I code this to split the JSON up to the different pages?
Thanks heaps for any help.
Cheers,
Paul
You can you groupBy function from 'package:collection/collection.dart'
var json = {
"rows":[
{ "JOBID":23, "JOB_DATE":1588809600000, "START_TIME":"07:30", "JOB_NAME":"Cleaner" },
{ "JOBID":24, "JOB_DATE":1588809600000, "START_TIME":"08:30", "JOB_NAME":"Manager" }
{ "JOBID":25, "JOB_DATE":1588809600000, "START_TIME":"12:30", "JOB_NAME":"Caretaker" }
{ "JOBID":26, "JOB_DATE":1588896000000, "START_TIME":"08:30", "JOB_NAME":"Manager" }
{ "JOBID":27, "JOB_DATE":1588896000000, "START_TIME":"13:30", "JOB_NAME":"Caretaker" }
]
}
List<Map<String, dynamic> rows = json['rows']
Map<dynamic, List<Map<String, dynamic>> sortedRow = groupBy(rows, (row) => row['JOB_DATE']
And you will have a map where keys are (1588809600000, 1588896000000) and values are lists of your objects.
After that you can create PageView with a ListView of your objects
I would use something like this Json to dart class converter to quickly get a dart class for you JSON data structure. Then I would use the resulting dart class to parse your Json into a list of jobs in dart and then use that list on a specific page's ListView data source by only selecting the values with the specific sTARTTIME value you want to display on that page.
class Job {
List<Rows> rows;
Job({this.rows});
Job.fromJson(Map<String, dynamic> json) {
if (json['rows'] != null) {
rows = new List<Rows>();
json['rows'].forEach((v) {
rows.add(new Rows.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
if (this.rows != null) {
data['rows'] = this.rows.map((v) => v.toJson()).toList();
}
return data;
}
}
class Rows {
int jOBID;
int jOBDATE;
String sTARTTIME;
String jOBNAME;
Rows({this.jOBID, this.jOBDATE, this.sTARTTIME, this.jOBNAME});
Rows.fromJson(Map<String, dynamic> json) {
jOBID = json['JOBID'];
jOBDATE = json['JOB_DATE'];
sTARTTIME = json['START_TIME'];
jOBNAME = json['JOB_NAME'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['JOBID'] = this.jOBID;
data['JOB_DATE'] = this.jOBDATE;
data['START_TIME'] = this.sTARTTIME;
data['JOB_NAME'] = this.jOBNAME;
return data;
}
}
You can parse our JSON like this in your flutter app:
import 'dart:convert';
...
Job jobs = Job.fromJson(json.decode(jsonString));
...
final firstPageData = jobs.rows.where((row) => row.jOBDATE == 1588809600000).toList();
final secondPageData = jobs.rows.where((row) => row.jOBDATE == 1588896000000).toList();
I can't stream (read) Documents that contain array properties that are Maps in Firestore.
Using Firestore with a document containing an array with a simple String type works as expected. Easy to write (append with FieldValue.arrayUnion(['data1','data2','data3']) and stream back out with code like:
var test2 = List<String>();
for (var item in data['items']) {
print('The item $item');
test2.add(item);
}
test2 can now be used as my items property.
When I try and use a List where item type becomes a Map in Firestore and is just a simple Class containing a few Strings and a date property. I can write these to FireStore but I can't read them back out.
The following code fails: (no errors in the Debug Console but it doesn't run)
var test2 = List<Item>();
data['items'].forEach((item) {
var description = item['description'];
print('The description $description'); // <-- I don't get past this
final x = Item.fromMap(item); // <-- so I can't even attempt this
return test2.add(x);
});
I never get to actually call my Item.fromMap constructor: here is another try:
// fails
final theItems = data['items'].map((x) {
return Item.fromMap(x); // <-- never get here
});
Although the DEBUG CONSOLE doesn't say there is any problem. If I inspect theItems variable (the debugger 'jumps' a couple of lines down to my return) after the failed iteration it looks like this:
MappedListIterable
_f:Closure
_source:List (1 item)
first:Unhandled exception:\ntype '_InternalLinkedHashMap<dynamic, dynamic>' is not a subtype of type 'Map<String, dynamic>'\n#0 new Listing.fromMap.<anonymous closure> isEmpty:false
isNotEmpty:true
iterator:ListIterator
last:Unhandled exception:\ntype '_InternalLinkedHashMap<dynamic, dynamic>' is not a subtype of
So bad things have happened but I have no idea why!
Has anyone actually written and retrieved Firestore array properties that contain Maps?
Any help in how to proceed would be greatly appreciated!
More: screen shot of the document
Here is the code that reads (streams) the collection
Stream<List<T>> collectionStream<T>({
#required String path,
#required T builder(Map<String, dynamic> data, String documentID),
#required String userId,
Query queryBuilder(Query query),
int sort(T lhs, T rhs),
}) {
Query query = Firestore.instance.collection(path);
if (queryBuilder != null) {
query = queryBuilder(query);
}
final Stream<QuerySnapshot> snapshots = query.snapshots();
return snapshots.map((snapshot) {
//print('document: path $path ${snapshot.documents[0]?.documentID}');
final result = snapshot.documents
.map((snapshot) => builder(snapshot.data, snapshot.documentID))
.where((value) => value != null)
.toList();
if (sort != null) {
result.sort(sort);
}
print('returning from CollectionStream');
return result;
});
}
the .map is where the problem comes in. The builder function resolves to this:
builder: (data, documentId) {
return Listing.fromMap(data, documentId);
},
Which ends up here
factory Listing.fromMap(
Map<String, dynamic> data,
String documentId,
) {
if (data == null) {
return null;
}
final theTime = (data['createdAt'] as Timestamp).toDate();
// see above code where I fail at getting at the items property/field
Here is the Item Class:
class Item {
Item(this.description, this.imageURL, this.thumbnailURL,
{this.status = ItemStatus.active,
this.type = ListingType.free,
this.price = 0,
this.isUpdate = false,
this.createdAt});
final String description;
final String imageURL;
final String thumbnailURL;
final ItemStatus status;
final ListingType type;
final int price;
final bool isUpdate;
DateTime createdAt;
factory Item.fromMap(
Map<String, dynamic> data,
) {
if (data == null) {
return null;
}
final theTime = (data['createdAt'] as Timestamp).toDate();
return Item(
data['description'],
data['imageURL'],
data['thumbnailURL'],
status: _itemStatusFromString(data['status']),
type: data['type'] == 'free' ? ListingType.free : ListingType.flatRate,
createdAt: theTime,
);
}
Map<String, dynamic> toMap() {
// enums are for shit in Dart
final statusString = status.toString().split('.')[1];
final typeString = type.toString().split('.')[1];
return {
'description': description,
'imageURL': imageURL,
'thumbnailURL': thumbnailURL,
'itemStatus': statusString,
'price': price,
'listingType': typeString,
if (!isUpdate) 'createdAt': DateTime.now(),
if (isUpdate) 'updatedAt': DateTime.now(),
};
}
}
The above is never called to read (we crash) ... it is called to write the data.
This is a known issue with Firestore. Here is the starting thread Flutter issues I found
From reading through the issues it seems lots of folks use a serializer package which is where the issue surfaced. Its still active...
Here is my solution NOT using a serializer and just doing it 'by hand'.
I was able simplify the problem and generate an error that was Google-able. Below is a single page which just writes and reads a single Document. The Class A and B are as small as can be.
So the code writes a single document to Firestore the contains two properties. A name and items. The items being the List of Class B that Class A contains. Checkout the Firebase console. The reading just does that.
No StreamController just the Console. The problem is in the fromMap method where we have to convert the array of objects in Firesote to a List of class instances in Dart. This should not be this difficult and at a minimum it should be documented ....
the line
var theRealItems = data['items'].map((i) => B.fromMap(i));
will generate the error. And needs to be replaced with
var theItems = data['items'].map((i) {
var z = Map<String, dynamic>.from(i);
print(z['description']);
return B.fromMap(z);
}).toList();
var theRealItems = List<B>.from(theItems);
Why this is so difficult is still a mystery to me! Anyone improving this code: I'm all ears.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class A {
A(this.name, this.items);
final name;
final List<B> items;
factory A.fromMap(
Map<String, dynamic> data,
String documentId,
) {
if (data == null) {
return null;
}
// we crash here !!!
var theRealItems = data['items'].map((i) => B.fromMap(i));
// uncomment the 6 lines below
// var theItems = data['items'].map((i) {
// var z = Map<String, dynamic>.from(i);
// print(z['description']);
// return B.fromMap(z);
// }).toList();
// var theRealItems = List<B>.from(theItems);
return A(data['name'], theRealItems);
}
Map<String, dynamic> toMap() {
var theItems = items.map((i) => i.toMap()).toList();
return {'name': name, 'items': theItems};
}
}
class B {
B(this.description);
final description;
factory B.fromMap(
Map<String, dynamic> data,
) {
if (data == null) {
return null;
}
return B(data['description']);
}
Map<String, dynamic> toMap() {
return {
'description': description,
};
}
}
class Test extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
onPressed: () => _write(),
child: Text('Write the Doc'),
),
RaisedButton(
onPressed: () => _read(),
child: Text('Read the Doc ... check Debug Console!'),
),
],
),
),
);
}
_write() async {
try {
var b = B('Inside B!');
List<B> theList = List<B>();
theList.add(b);
var a = A('myName', theList);
await Firestore.instance
.collection('test')
.document('testDoc')
.setData(a.toMap());
print('returning from write!');
} catch (e) {
print('Error ${e.toString()}');
}
}
_read() async {
try {
var aFromFs = await Firestore.instance
.collection('test')
.document('testDoc')
.get();
var a = A.fromMap(aFromFs.data, aFromFs.documentID);
print('the A from FireBase $a with name ${a.name} first item ${a.items.first.description}');
print('returning from read!');
} catch (e) {
print('Oh no Error! ${e.toString()}');
}
}
}
In the below code Name refers to the name of your respective document and collection.
Let's say you want to get "imageURL" and "thumbnailUrl" for now and update the values without deleting or changing other fields inside the array.
String imageUrl ;
String thumbnailUrl;
DocumentReference _docReference = FirebaseFirestore.instance.collection(NAME).doc(NAME);
//refering to the specific document
Map<String, dynamic> neededData= allDocsFromTraining.data();
//getting all the keys and values inside the document
List<Map<String, dynamic>> _yourDocument=
(neededData["items"] as List<dynamic>)
.map((m) => Map<String, dynamic>.from(m))
.toList();
//only getting the list of items
for (int index = 0; index < _yourDocument.length; index++) {
Map<String, dynamic> _getMap = _yourDocument[index];
if (_getMap["price"] == 0)
//giving a condition to get the desired value and make changes
{
//setting the variables with the value from the database
setState(() {
imageUrl = _getMap["imageURL"];
thumbnailUrl = _getMap["thumbnailURL"]
});
///Use this if you want to update value of imageURL and thumbnailURL at that specific index of an array
_getMap['imageURL'] = "Your Updated URL";
_getMap['thumbnailURL'] = "Your Updated Url;
break;
} else {}
}
await _docReference.update({"items": _yourDocument});
//this adds the updated value to your database.