How to perform "add to cart" function from product detail screen? [Flutter][Dart]

Viewed 19

I followed LogRocket's shopping cart tutorial to make a cart and add to cart function for my shopping app school project. His tutorial can be found here: https://blog.logrocket.com/building-shopping-cart-flutter/#make-cart-screen

The problem I'm currently facing is, I'm trying to implement my add-to-cart button from my product detail screen and his tutorial above shows an add-to-cart function with the various buttons from a list of products; it looks like this: enter image description here

But I'm trying to implement the same add-to-cart function from my product detail screen, with the cart button on each of the screens. So far, using his code as a guide, I've managed to view the cart button on the product detail screen, but my code is unable to access the productid and perform the add to cart function.

I did some research and someone mentioned that to do this, I need to access the provider in the product detail page and add it in the cart by calling the method and passing the cart item.

I'm really new to coding and since I don't have much time to learn everything from scratch and redo a cart function on my own for this project, I tried to edit this tutorial code to fit my own, but the cart function has not worked yet for me (but all dart analysis errors are cleared). Please share if you have any ideas on how I could go about changing up this code to fit my own! Thank you so much!

Currently this is the code I have for my product detail screen:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:MyShoppingApp/provider/CartProvider.dart';
import 'package:MyShoppingApp/db/cart_database.dart';
import 'package:MyShoppingApp/model/cart.dart';

//add product data
import 'model/products_repository.dart';
import '../model/cart.dart';

class ProductDetailsPage extends StatelessWidget {

  static const routeName = '/user-products';
   ProductDetailsPage({Key? key}) : super(key: key); //const
  DBHelper dbHelper = DBHelper();
  List loadedProduct = [];

  @override
  Widget build(BuildContext context) {

    //get particular productId using the ModalRoute class
    final productId = ModalRoute.of(context)!.settings.arguments as String;
    print(productId);
    //use Provider package to find out ID by accessing method declared in Product()
    final loadedProduct = ProductsRepository().findById(productId);
    // final loadedProduct = Provider.of<Product>(
    //   context,
    //   listen: true,
    // ).findById(productId); //use ProductDetailsScreen to tap the ID of product

    //List<bool> clicked = List.generate(10, (index) => false, growable: true);
      final cart = Provider.of<CartProvider>(context);
      void saveData(int index) {
        dbHelper
            .insert(
          CartItem(
            id: index,
            title: loadedProduct.name,
            price: loadedProduct.price.toDouble(),
            quantity: ValueNotifier(1),
            image: loadedProduct.image,
          ),
        )
            .then((value) {
          cart.addTotalPrice(loadedProduct.price.toDouble());
          cart.addCounter();
          print('Product Added to cart');
        }).onError((error, stackTrace) {
          print(error.toString());
        });
      }

    return Scaffold(
      backgroundColor: Colors.orange[50],
      appBar: AppBar(
        backgroundColor: Colors.deepOrange[900],
        title: const Text("Product details "),
        leading: IconButton(
          icon: const Icon(
            Icons.arrow_back_ios_outlined,
            color: Colors.black,
            semanticLabel: 'back to home',
          ),
          onPressed: () {
            Navigator.pop(context);
          },
        ),
      ),
        //body:
        body: ListView.builder(
            padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 8.0),
            shrinkWrap: true,
           // itemCount: loadedProduct.length,
            itemBuilder: (context, index) {
              return Card(
                //SingleChildScrollView(
                child: Column(
                  children: <Widget>[
                    SizedBox(
                      height: 300,
                      width: double.infinity,
                      child: Image.asset(
                        loadedProduct.image,
                        fit: BoxFit.cover,
                      ),
                    ),
                    const SizedBox(height: 10),
                    Text(
                      '\$${loadedProduct.price}',
                      style: const TextStyle(
                        color: Colors.grey,
                        fontSize: 20,
                      ),
                    ),
                    const SizedBox(
                      height: 10,
                    ),
                    ElevatedButton(
                        style: ElevatedButton.styleFrom(
                            primary: Colors.blueGrey.shade900),
                        onPressed: () {
                          saveData(index);
                        },
                        child: const Text('Add to Cart')),
                    Container(
                      padding: const EdgeInsets.symmetric(horizontal: 10),
                      width: double.infinity,
                      child: Text(
                        loadedProduct.description,
                        textAlign: TextAlign.center,
                        softWrap: true,
                      ),
                    ),
                  ],
                ),
              );
            })
    );
  }
  }

CartProvider dart file:


import 'package:flutter/cupertino.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:flutter/material.dart';
import '../model/cart.dart';
import 'package:MyShoppingApp/db/cart_database.dart';

class CartProvider with ChangeNotifier {
  DBHelper dbHelper = DBHelper();
  int _counter = 0;
  int _quantity = 1;
  int get counter => _counter;
  int get quantity => _quantity;

  double _totalPrice = 0.0;
  double get totalPrice => _totalPrice;

  List<CartItem> cart = [];

  Future<List<CartItem>> getData() async {
    cart = await dbHelper.getCartList();
    notifyListeners();
    return cart;
  }

  void _setPrefsItems() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.setInt('cart_items', _counter);
    prefs.setInt('item_quantity', _quantity);
    prefs.setDouble('total_price', _totalPrice);
    notifyListeners();
  }

  void _getPrefsItems() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    _counter = prefs.getInt('cart_items') ?? 0;
    _quantity = prefs.getInt('item_quantity') ?? 1;
    _totalPrice = prefs.getDouble('total_price') ?? 0;
  }

  void addCounter() {
    _counter++;
    _setPrefsItems();
    notifyListeners();
  }

  void removeCounter() {
    _counter--;
    _setPrefsItems();
    notifyListeners();
  }

  int getCounter() {
    _getPrefsItems();
    return _counter;
  }

  void addQuantity(int id) {
    final index = cart.indexWhere((element) => element.id == id);
    cart[index].quantity!.value = cart[index].quantity!.value + 1;
    _setPrefsItems();
    notifyListeners();
  }

  void deleteQuantity(int id) {
    final index = cart.indexWhere((element) => element.id == id);
    final currentQuantity = cart[index].quantity!.value;
    if (currentQuantity <= 1) {
      currentQuantity == 1;
    } else {
      cart[index].quantity!.value = currentQuantity - 1;
    }
    _setPrefsItems();
    notifyListeners();
  }

  void removeItem(int id) {
    final index = cart.indexWhere((element) => element.id == id);
    cart.removeAt(index);
    _setPrefsItems();
    notifyListeners();
  }

  int getQuantity(int quantity) {
    _getPrefsItems();
    return _quantity;
  }

  void addTotalPrice(double productPrice) {
    _totalPrice = _totalPrice + productPrice;
    _setPrefsItems();
    notifyListeners();
  }

  void removeTotalPrice(double productPrice) {
    _totalPrice = _totalPrice - productPrice;
    _setPrefsItems();
    notifyListeners();
  }

  double getTotalPrice() {
    _getPrefsItems();
    return _totalPrice;
  }
}

and Product Repository dart file:

import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:MyShoppingApp/db/cart_database.dart';
//add product data
import 'package:MyShoppingApp/model/product.dart';

//to get all products at once or any particular product by its ID
//product class that uses mixins with ChangeNotifier
class ProductsRepository with ChangeNotifier {
  DBHelper dbHelper = DBHelper();

  static List<Product> loadProducts(Category category) {
    //linked list storing objects of type Product
    var allProducts = <Product>[
      Product(
        category: Category.accessories,
        id: "0",
        isFeatured: true,
        name: 'Vagabond sack',
        price: 120,
        details: "Nice fancy shirt",
        description: "Comfortable and minimalistic",
        image: "packages/shrine_images/0-0.jpg",
      ),
      Product(
        category: Category.accessories,
        id: "1",
        isFeatured: true,
        name: 'Stella sunglasses',
        price: 58,
        details: "",
        description: "",
        image: "packages/shrine_images/1-0.jpg",
      ),
      Product(
        category: Category.accessories,
        id: "2",
        isFeatured: false,
        name: 'Whitney belt',
        price: 35,
        details: "",
        description: "",
        image: "packages/shrine_images/2-0.jpg",
      ),
      Product(
        category: Category.accessories,
        id: "3",
        isFeatured: true,
        name: 'Garden strand',
        price: 98,
        details: "",
        description: "",
        image: "packages/shrine_images/3-0.jpg",
      ),
      Product(
        category: Category.accessories,
        id: "4",
        isFeatured: false,
        name: 'Strut earrings',
        price: 34,
        details: "",
        description: "",
        image: "packages/shrine_images/4-0.jpg",
      ),//removed other products to save space
    ];

    if (category == Category.all) {
      return allProducts;
    } else {
      return allProducts.where((Product p) {
        return p.category == category;
      }).toList();
    }
  }

  //to get particular products by ID
  Product findById(String id) {
    var x = loadProducts(Category.all).firstWhere((prod) => prod.id == id);
    print("findById successful");
    print(x);
    return x;
  }
  
  void addProduct() {
    // _items.add(value);
    notifyListeners();
  }

}

If you happen to be familiar with Dart or Flutter app developing please help me! Any kind of help would mean so much to me. Do let me know if I should include any other dart files or details to make things clearer! Thank you so much!

(forgot to add cart screen dart file as well):

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../model/cart.dart';
import 'package:MyShoppingApp/db/cart_database.dart';
import 'package:MyShoppingApp/provider/CartProvider.dart';

class CartPage extends StatefulWidget {
  const CartPage({
    Key? key,
  }) : super(key: key);
  static const routeName = '/cart';
  @override
  State<CartPage> createState() => _CartPageState();
}

class _CartPageState extends State<CartPage> {
  DBHelper? dbHelper = DBHelper();
  List<bool> tapped = [];

  @override
  void initState() {
    super.initState();
    context.read<CartProvider>().getData();
  }

  @override
  Widget build(BuildContext context) {
    final cart = Provider.of<CartProvider>(context);
    return Scaffold(
      backgroundColor:Colors.orange[100],
      appBar: AppBar(
        backgroundColor: Colors.deepOrange[900],
        centerTitle: true,
        title: const Text('My Shopping Cart'),
        actions: const [
          SizedBox(
            width: 20.0,
          ),
        ],
          leading: IconButton(
            icon: const Icon(
              Icons.arrow_back_ios_outlined,
              color: Colors.black,
              semanticLabel: 'back to home',
              ),
              onPressed: () {
            Navigator.pop(context);
                  },
        ),
      ),
        body: Column(
        children: [
          Expanded(
            child: Consumer<CartProvider>(
              builder: (BuildContext context, provider, widget) {
                if (provider.cart.isEmpty) {
                  return const Center(
                      child: Text(
                        'Your Cart is Empty',
                        style:
                        TextStyle(fontWeight: FontWeight.bold, fontSize: 18.0),
                      ));
                } else {
                  return ListView.builder(
                      shrinkWrap: true,
                      itemCount: provider.cart.length,
                      itemBuilder: (context, index) {
                        return Card(
                          color: Colors.blueGrey.shade200,
                          elevation: 5.0,
                          child: Padding(
                            padding: const EdgeInsets.all(4.0),
                            child: Row(
                              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                              mainAxisSize: MainAxisSize.max,
                              children: [
                                Image(
                                  height: 80,
                                  width: 80,
                                  image:
                                  AssetImage(provider.cart[index].image!),
                                ),
                                SizedBox(
                                  width: 130,
                                  child: Column(
                                    crossAxisAlignment:
                                    CrossAxisAlignment.start,
                                    children: [
                                      const SizedBox(
                                        height: 5.0,
                                      ),
                                      RichText(
                                        overflow: TextOverflow.ellipsis,
                                        maxLines: 1,
                                        text: TextSpan(
                                            text: 'Name: ',
                                            style: TextStyle(
                                                color: Colors.blueGrey.shade800,
                                                fontSize: 16.0),
                                            children: [
                                              TextSpan(
                                                  text:
                                                  '${provider.cart[index].title!}\n',
                                                  style: const TextStyle(
                                                      fontWeight:
                                                      FontWeight.bold)),
                                            ]),
                                      ),
                                      RichText(
                                        maxLines: 1,
                                        text: TextSpan(
                                            text: 'Price: ' r"$",
                                            style: TextStyle(
                                                color: Colors.blueGrey.shade800,
                                                fontSize: 16.0),
                                            children: [
                                              TextSpan(
                                                  text:
                                                  '${provider.cart[index].price!}\n',
                                                  style: const TextStyle(
                                                      fontWeight:
                                                      FontWeight.bold)),
                                            ]),
                                      ),
                                    ],
                                  ),
                                ),
                                ValueListenableBuilder<int>(
                                    valueListenable:
                                    provider.cart[index].quantity!,
                                    builder: (context, val, child) {
                                      return PlusMinusButtons(
                                        addQuantity: () {
                                          cart.addQuantity(
                                              provider.cart[index].id!);
                                          dbHelper!
                                              .updateQuantity(CartItem(
                                            id: index,
                                           // id: index.toString(),
                                            title: provider
                                                .cart[index].title,
                                            price: provider
                                                .cart[index].price,
                                            quantity: ValueNotifier(
                                                provider.cart[index]
                                                    .quantity!.value),
                                            image: provider
                                                .cart[index].image,
                                          ))
                                              .then((value) {
                                            setState(() {
                                              cart.addTotalPrice(double.parse(
                                                  provider
                                                      .cart[index].price
                                                      .toString()));
                                            });
                                          });
                                        },
                                        deleteQuantity: () {
                                          cart.deleteQuantity(
                                              provider.cart[index].id!);
                                          cart.removeTotalPrice(double.parse(
                                              provider.cart[index].price
                                                  .toString()));
                                        },
                                        text: val.toString(),
                                      );
                                    }),
                                IconButton(
                                    onPressed: () {
                                      dbHelper!.deleteCartItem(
                                          provider.cart[index].id!);
                                      provider
                                          .removeItem(provider.cart[index].id!);
                                      provider.removeCounter();
                                    },
                                    icon: Icon(
                                      Icons.delete,
                                      color: Colors.red.shade800,
                                    )),
                              ],
                            ),
                          ),
                        );
                      });
                }
              },
            ),
          ),
          Consumer<CartProvider>(
            builder: (BuildContext context, value, Widget? child) {
              final ValueNotifier<int?> totalPrice = ValueNotifier(null);
              for (var element in value.cart) {
                totalPrice.value =
                    ((element.price! * element.quantity!.value) +
                        (totalPrice.value ?? 0)) as int?;
              }
              return Column(
                children: [
                  ValueListenableBuilder<int?>(
                      valueListenable: totalPrice,
                      builder: (context, val, child) {
                        return ReusableWidget(
                            title: 'Sub-Total',
                            value: r'$' + (val?.toStringAsFixed(2) ?? '0'));
                      }),
                ],
              );
            },
          )
        ],
      ),
      bottomNavigationBar: InkWell(
        onTap: () {
          ScaffoldMessenger.of(context).showSnackBar(
            const SnackBar(
              content: Text('Payment Successful'),
              duration: Duration(seconds: 4),
            ),
          );
        },
        child: Container(
          color: Colors.orange.shade800,
          alignment: Alignment.center,
          height: 50.0,
          child: const Text(
            'Proceed to Pay',
            style: TextStyle(
              fontSize: 18.0,
              fontWeight: FontWeight.bold,
            ),
          ),
        ),
      ),
    );
  }
}

class PlusMinusButtons extends StatelessWidget {
  final VoidCallback deleteQuantity;
  final VoidCallback addQuantity;
  final String text;
  const PlusMinusButtons(
      {Key? key,
        required this.addQuantity,
        required this.deleteQuantity,
        required this.text})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        IconButton(onPressed: deleteQuantity, icon: const Icon(Icons.remove)),
        Text(text),
        IconButton(onPressed: addQuantity, icon: const Icon(Icons.add)),
      ],
    );
  }
}

class ReusableWidget extends StatelessWidget {
  final String title, value;
  const ReusableWidget({Key? key, required this.title, required this.value});

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(8.0),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: [
          Text(
            title,
            style: Theme.of(context).textTheme.subtitle1,
          ),
          Text(
            value.toString(),
            style: Theme.of(context).textTheme.subtitle2,
          ),
        ],
      ),
    );
  }
}
0 Answers
Related