Reputation: 1752
I was trying to make a simple cart app which loads cart data from database. actually loads data from database to provider. but when we add item to cart and close the app, when It comes back cart hasn't any item.
fetchCartProducts called to get data from database, it must give to the provider cartItems. but it stores data in database, not showing it when it's closed.
home_screen.dart
class HomeScreen extends StatefulWidget {
@override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
Future<List<FoodItem>> _foodItems;
@override
void initState() {
super.initState();
_foodItems = ApiService.getFoodItems();
Provider.of<CartProvider>(context, listen: false).fetchCartProducts();
}
@override
Widget build(BuildContext context) {
final cart = Provider.of<CartProvider>(context, listen: false);
return Scaffold(
appBar: AppBar(
title: const Text('Food Cart'),
actions: [
Consumer<CartProvider>(
builder: (_, cartprovider, ch) => Badge(
child: ch,
value: cartprovider.itemCount.toString(),
),
child: IconButton(
icon: Icon(Icons.shopping_cart),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (_) {
return CartScreen();
}),
);
},
),
),
],
),
body: FutureBuilder<List<FoodItem>>(
future: _foodItems,
builder: (conext, snapshot) => !snapshot.hasData
? const Center(
child: CircularProgressIndicator(),
)
: ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
FoodItem foodItem = snapshot.data[index];
return ListTile(
title: Text(foodItem.productName),
subtitle: Text(foodItem.variant),
trailing: IconButton(
onPressed: () {
cart.addToCart(
foodItem.storeid.toString(),
foodItem.productName,
1,
foodItem.price,
);
setState(() {});
},
icon: const Icon(Icons.shopping_cart),
),
);
},
),
),
);
}
}
cart_provider.dart
class CartProvider with ChangeNotifier {
Map<String, CartModel> _cartItems = {};
Map<String, CartModel> get cartItems {
return {..._cartItems};
}
int get itemCount {
return _cartItems.length;
}
void reduceItem(
String productId,
String productName,
int quantity,
double price,
) {
if (quantity == 1) {
_cartItems.remove(productId);
notifyListeners();
} else {
_cartItems.update(
productId,
(cartItem) => CartModel(
productId,
cartItem.price,
productName,
cartItem.quantity - 1,
),
);
notifyListeners();
}
}
void addItem(String productId, String productName, double price) {
if (_cartItems.containsKey(productId)) {
//add quantity
_cartItems.update(productId, (existingCartItem) {
return CartModel(
existingCartItem.id,
existingCartItem.price,
existingCartItem.productName,
existingCartItem.quantity + 1,
);
});
}
notifyListeners();
}
void addToCart(
String productId, String productName, int quantity, double price) {
_cartItems.putIfAbsent(
productId,
() {
DBHelper.insert('cart_food', {
'id': productId,
'productName': productName,
'quantity': quantity,
'price': price,
});
return CartModel(
productId,
price,
productName,
1,
);
},
);
notifyListeners();
}
double get totalAmount {
var total = 0.0;
_cartItems.forEach((key, cartItem) {
total += cartItem.price * cartItem.quantity;
});
return total;
}
void removeItem(String productId) async {
_cartItems.remove(productId);
notifyListeners();
await DBHelper.delete(productId);
}
Future<void> fetchCartProducts() async {
List<Map<String, CartModel>> dataList = await DBHelper.getData('cart_food');
print(dataList);
//_cartItems = dataList.map((e) => )
_cartItems = dataList as Map<String, CartModel>;
}
}
getData
static Future<List<Map<String, CartModel>>> getData(String table) async {
final db = await DBHelper.database();
return db.query(table);
}
What did I wrong? I don't get it. please help
Upvotes: 0
Views: 466
Reputation: 53
Is the data successfully uploaded to the database?
If so, here's what could be going wrong -- You're fetching the products in initState() as follows:
Provider.of<CartProvider>(context, listen: false).fetchCartProducts();
The problem here is "context". Generally, for a widget the perfect place to initialize and listen for data from the web is the initState() method. However "context" doesn't work in initState() because "context" comes into play after initState() is called. So, you can't invoke your provider class there. Try to use didChangeDependencies instead as follows:
var _isInit=true;
@override
void didChangeDependencies() {
if(_isInit){
_foodItems = ApiService.getFoodItems();
Provider.of<CartProvider>(context,listen:false).fetchCartProducts();
_isInit=false;
}
super.didChangeDependencies();
}
Lmk if this works.
Upvotes: 1