Flutter saving Map<String, Object> to Shared Preferences - flutter

This is my Code to store CartItems.
I want to store it in Shared Preferences as cartItems since on refresh, the data are being lost.
Also I want to have function in the CartItem to check if the Product is in the cart or not so that I can make it checked/unchecked.
class CartItem {
final String id;
final String prodID;
final String title;
final String img;
final double quantity;
final double price;
final double availQuantity;
CartItem({
#required this.id,
#required this.prodID,
#required this.title,
#required this.quantity,
#required this.price,
#required this.img,
this.availQuantity,
});
}
class Cart with ChangeNotifier {
Map<String, CartItem> _items = {};
Map<String, CartItem> get items {
return {..._items};
}
int get itemCount {
return _items.length;
}
void addItem(String productId, double price, String title, String img,
double availQuantity) {
if (_items.containsKey(productId)) {
_items.update(
productId,
(existingCartItem) => CartItem(
id: existingCartItem.id,
title: existingCartItem.title,
img: existingCartItem.img,
price: existingCartItem.price,
quantity: existingCartItem.quantity + 1,
availQuantity: existingCartItem.availQuantity,
),
);
} else {
_items.putIfAbsent(
productId,
() => CartItem(
id: productId,
title: title,
img: img,
price: price,
quantity: 1,
availQuantity: availQuantity,
),
);
}
notifyListeners();
}
}
I tried many ways to convert to string and call setString(),
convert to List too. but couldn't accomplish.
Help needed.

You can store it if you convert the Map to a String as follows:
import 'dart:convert';
...
String mapToStr = json.encode(map);
When you want to get the string out of shared preferences and use it as a Map again, decode it from String to Map again as follows:
Map<String, Object> strToMap = json.decode(mapToStr);

it's possible to see your function that save in sharedPreference,
Do you have try to convert tour Cart in Map<String, dynamic> and save. I think you try to save your directly your Cart class, but it's not allowed, you can only save int, string, List, Map ...

You can check from this code, if the product is in cart or not:
bool checkProductAddedToCart(productId) {
if (_items.containsKey(productId)) {
return true;
} else {
return false;
}
}

Related

Firebase Map type returns null in flutter

In Firebase, I have a field called stockBySize, as a map
stockBySize
M: "5"
L: "2"
In flutter app, thats my model:
class Product {
String? id;
String? category;
String? name;
String? description;
String? color;
String? price;
List<String>? images;
Map<String, String>? stockBySize;
factory Product.fromFirestore(
DocumentSnapshot<Map<String, dynamic>> snapshot,
SnapshotOptions? options,
) {
final data = snapshot.data();
return Product(
id: data?['id'],
category: data?['category'],
name: data?['name'],
description: data?['description'],
color: data?['color'],
price: data?['price'],
images: data?['images'] is Iterable ? List.from(data?['images']) : null,
stockBySize: data?['stockBySize'] is Iterable
? Map<String, String>.from(data?['stockBySize'])
: null,
);
}
Below is the code where I read the Firebase data:
Future<List<Product>> findProducts() async {
final query = db.collection("products").withConverter(
fromFirestore: Product.fromFirestore,
toFirestore: (Product product, _) => product.toFirestore(),
);
try {
final docSnap = await query.get();
List<Product> products = [];
docSnap.docs.forEach((element) {
products.add(
Product(
id: element.id,
category: element.data().category,
name: element.data().name,
description: element.data().description,
color: element.data().color,
price: element.data().price,
images: element.data().images,
stockBySize: element.data().stockBySize,
),
);
});
log(products.toString());
return products;
} on FirebaseException {
rethrow;
}
}
The problem: the field stockBySize is always null.
Whats wrong???? The field is written correctly and there is data in firebase
Try the following code:
class Product {
Product({
required this.id,
required this.category,
required this.name,
required this.description,
required this.color,
required this.price,
required this.images,
required this.stockBySize,
});
String? id;
String? category;
String? name;
String? description;
String? color;
String? price;
List<String>? images;
Map<String, dynamic>? stockBySize;
factory Product.fromFirestore(
DocumentSnapshot<Map<String, dynamic>> snapshot,
SnapshotOptions? options,
) {
final data = snapshot.data();
return Product(
id: data?['id'],
category: data?['category'],
name: data?['name'],
description: data?['description'],
color: data?['color'],
price: data?['price'],
images: data?['images'] is Iterable ? List.from(data?['images']) : null,
stockBySize: data?['stockBySize'] is Map<String, dynamic> ? Map<String, dynamic>.from(data?['stockBySize']) : null,
);
}
}

Adding models to a List keeps giving instance of that Model when I try to print and check the value

I am fetching data from firestore nd converting to a model, then adding to a list of items of that particular model. I noticed something was wrong, then I tried to check the value of the item in this List, I discovered they were all instances of that model class. Screen shot of the terminal is attached.
This is the code retrieving the data from Firestore.
List<ItemModel> allProducts = [];
Future<List<ItemModel>> getAllProducts() async {
final QuerySnapshot snapshot =
await FirebaseFirestore.instance.collection('Products').get();
List<ItemModel> _allProducts = [];
for (var doc in snapshot.docs) {
print(doc['title']);
_allProducts.add(ItemModel(
description: doc['description'],
id: doc['id'],
imagepath: doc['imagepath'],
price: doc['price'],
title: doc['title'],
amount: doc['amount']));
}
return _allProducts;
}
Future newList() async {
print(allProducts);
allProducts = await getAllProducts();
print(allProducts);
}
#override
void initState() {
newList();
super.initState();
}
This is the data converting the document snapshot from firebase to the Item Model class.
class ItemModel with ChangeNotifier {
final String title;
final int price;
bool isCarted;
final String id; // name of produce
int amount; // amount uploader
String description; // description of produce
String imagepath; // image of produce
bool isfavourited; // favorited produce
ItemModel({
required this.id,
this.isCarted = false,
required this.price,
required this.title,
this.amount = 1,
required this.description,
required this.imagepath,
this.isfavourited = false,
});
factory ItemModel.fromMap(Map map) {
return ItemModel(
id: map['id'],
isCarted: map['isCarted'],
isfavourited: map['isfavourited'],
amount: map['amount'],
price: map['price'],
title: map['title'],
description: map['description'],
imagepath: map['imagepath'],
);
}
factory ItemModel.fromFirestore(
DocumentSnapshot<Map<String, dynamic>> snapshot) {
final data = snapshot.data();
return ItemModel(
id: data!['id'],
isCarted: data['isCarted'] as bool,
isfavourited: data['isFavourited'] as bool,
amount: data['amount'] as int,
price: data['price'] as int,
title: data['title'],
description: data['description'],
imagepath: data['imagepath'],
);
}
The picture above shows the print statement on the console. printing these snapshots values on its own shows valid texts as also seen in the picture.
When you try to print a List of item objects in the console, it will always return the output you are receiving in your console. To print the individual values, you can either do:
for (ItemModel product in allProducts) {
// print all data members using product.<data-member>
print(product.title);
print(product.price);
}
or, you can define a toString() method override in the class, which which always display the String output of the object.
class ItemModel with ChangeNotifier {
final String title;
final int price;
bool isCarted;
final String id; // name of produce
int amount; // amount uploader
String description; // description of produce
String imagepath; // image of produce
bool isfavourited; // favorited produce
ItemModel({
required this.id,
this.isCarted = false,
required this.price,
required this.title,
this.amount = 1,
required this.description,
required this.imagepath,
this.isfavourited = false,
});
/// toString() override
#override
public String toString() {
return 'title: $title, price: $price'; // you can add more values to be printed
}
factory ItemModel.fromMap(Map map) {
return ItemModel(
id: map['id'],
isCarted: map['isCarted'],
isfavourited: map['isfavourited'],
amount: map['amount'],
price: map['price'],
title: map['title'],
description: map['description'],
imagepath: map['imagepath'],
);
}
factory ItemModel.fromFirestore(
DocumentSnapshot<Map<String, dynamic>> snapshot) {
final data = snapshot.data();
return ItemModel(
id: data!['id'],
isCarted: data['isCarted'] as bool,
isfavourited: data['isFavourited'] as bool,
amount: data['amount'] as int,
price: data['price'] as int,
title: data['title'],
description: data['description'],
imagepath: data['imagepath'],
);
}

flutter show late initialization error which flutter provider package?

why this error is shown on emulator ? it show in _items when debugging.
in flutter provider show error on emulator Late Initialization error : field _ items has not been initialize. it show testing/ error
class CartItem {
final String id;
final String title;
final int quantity;
final double price;
CartItem({
required this.id,
required this.title,
required this.quantity,
required this.price,
});
}
class Cart with ChangeNotifier {
late Map<String, CartItem> _items;
Map<String, CartItem> get items {
return {..._items};
}
int get iteamCount {
return _items.length;
}
void addItem(String productId, double price, String title) {
if (_items.containsKey(productId)) {
//change quantity
_items.update(
productId,
(existingCartItem) => CartItem(
id: existingCartItem.id,
title: existingCartItem.title,
quantity: existingCartItem.quantity + 1,
price: existingCartItem.price,
));
} else {
_items.putIfAbsent(
productId,
() => CartItem(
id: DateTime.now().toString(),
title: title,
price: price,
quantity: 1,
));
}
}
}
_items should be initialized before used.
Or you can just replace late Map<String, CartItem> _items with Map<String, CartItem> _items = {}.

I m very much confused seeing a statement of Map in flutter project

I am following a tutor channel..almost going we'll but confused at a statement
there is a provider class named cart.. and as we know,,, cart contains more than one cart items...but in code...tutor has given Map<String,CartItem> items={} and I think he should take List<Map<String,CartItem>> items=[];
here is a code
import 'package:flutter/foundation.dart';
class CartItem {
final String id;
final String title;
final int quantity;
final double price;
CartItem({
#required this.id,
#required this.title,
#required this.quantity,
#required this.price,
});
}
class Cart with ChangeNotifier {
Map<String, CartItem> _items = {};
Map<String, CartItem> get items {
return {..._items};
}
int get itemCount {
return _items.length;
}
void addItem(
String productId,
double price,
String title,
) {
if (_items.containsKey(productId)) {
// change quantity...
_items.update(
productId,
(existingCartItem) => CartItem(
id: existingCartItem.id,
title: existingCartItem.title,
price: existingCartItem.price,
quantity: existingCartItem.quantity + 1,
),
);
} else {
_items.putIfAbsent(
productId,
() => CartItem(
id: DateTime.now().toString(),
title: title,
price: price,
quantity: 1,
),
);
}
notifyListeners();
}
}

"Non-nullable instance field '_items' must be initialized.\nTry adding an initializer it

I am trying to make a modal for shopping cart but this gives me an error.
my dart sdk version:2.12.0>3.0.0
I tried using 'late' before this map but it giving me error in output result that lateinitialization error: field '_items' has not be initialized
Map<String, CartItem> _items;
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
class CartItem with ChangeNotifier {
final String id;
final String title;
final int quantity;
final double price;
CartItem(
{required this.id,
required this.title,
required this.price,
required this.quantity});
}
class Cart with ChangeNotifier {
Map<String, CartItem> _items; //error _items
Map<String, CartItem> get items {
return {..._items};
}
int get itemcount {
return _items.length;
}
void addItems(String productId, double price, String title) {
if (_items.containsKey(productId)) {
_items.update(
productId,
(existing) => CartItem(
id: existing.id,
title: existing.title,
price: existing.price,
quantity: existing.quantity + 1),
);
} else {
_items.putIfAbsent(
productId,
() => CartItem(
id: DateTime.now().toString(),
title: title,
price: price,
quantity: 1));
}
}
}
Initialize _items variable with {} empty map.
Map<String, CartItem> _items = {};