Search code examples
flutterdarte-commerce

Why is my add to cart counter not working in flutter?


I'm trying to make an e-commerce app using Flutter, I have implemented add to cart with the help of chatGPT since I'm new to it. Everything works okay, the only problem is that the counter on quantity isn't working, so if I press + to add, it won't add and same goes for -. Here's the code for the Cart Model, Cart Screen, and Cart Controller. I'm also using GetX for state management.

Cart Screen:

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:exclusive_details_and_restorations/const/cart_controller.dart';
import 'package:exclusive_details_and_restorations/const/text.dart';

class CartScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final CartController cartController = Get.find<CartController>();
    print('CartScreen rebuilt');
    return Container(
      decoration: BoxDecoration(
        gradient: LinearGradient(
          begin: Alignment.topLeft,
          end: Alignment.bottomRight,
          colors: [
            const Color.fromRGBO(0, 40, 70, 100),
            Colors.red.shade900,
          ],
        ),
      ),
      child: Scaffold(
        backgroundColor: const Color.fromRGBO(0, 40, 70, 100),
        appBar: AppBar(
          iconTheme: const IconThemeData(color: Colors.white),
          backgroundColor: const Color.fromRGBO(0, 40, 70, 100),
          title: const CustomText(
            text: "Cart",
            color: Colors.white,
          ),
          actions: [
            SizedBox(
              width: 100,
              child: Image.asset("assets/images/exclusive-details-rename.png"),
            ),
          ],
        ),
        body: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Obx(
            () => Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Expanded(
                  child: ListView.builder(
                    itemCount: cartController.cartItems.length,
                    itemBuilder: (context, index) {
                      var item = cartController.cartItems[index];
                      return ListTile(
                        title: CustomText(text: item.productName),
                        subtitle: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            CustomText(text: "\$${item.price}"),
                            Row(
                              children: [
                                ElevatedButton(
                                  onPressed: () {
                                    // Decrease quantity
                                    cartController.decreaseQuantity(item);
                                    cartController.update();
                                  },
                                  child: const Icon(Icons.remove),
                                ),
                                Padding(
                                  padding: const EdgeInsets.symmetric(
                                      horizontal: 8.0),
                                  child: CustomText(
                                      text: item.quantity.toString()),
                                ),
                                ElevatedButton(
                                  onPressed: () {
                                    // Increase quantity
                                    item.increaseQuantity();
                                    cartController.update();
                                  },
                                  child: const Icon(Icons.add),
                                ),
                              ],
                            ),
                          ],
                        ),
                      );
                    },
                  ),
                ),
                const Divider(),
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    CustomText(
                      text: "Total: ",
                      size: 18,
                      fontWeight: FontWeight.bold,
                    ),
                    CustomText(
                      text:
                          "\$${cartController.totalAmount.toStringAsFixed(2)}",
                      size: 18,
                      fontWeight: FontWeight.bold,
                    ),
                  ],
                ),
                const SizedBox(height: 20),
                ElevatedButton(
                  onPressed: () {
                    // Implement the checkout logic here
                  },
                  child: const Padding(
                    padding: EdgeInsets.all(8.0),
                    child: CustomText(
                      text: "Proceed to Checkout",
                      size: 16,
                      color: Colors.white,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

Cart Controller Code:

// controllers/cart_controller.dart
import 'package:exclusive_details_and_restorations/models/cart_model.dart';
import 'package:get/get.dart';

class CartController extends GetxController {
  RxList<CartItem> cartItems = <CartItem>[].obs;

  void addItem(CartItem newItem) {
    int existingIndex = cartItems.indexWhere(
      (item) => item.productName == newItem.productName,
    );

    if (existingIndex != -1) {
      cartItems[existingIndex].quantity += 1;
    } else {
      cartItems.add(newItem);
    }
  }

  void decreaseQuantity(CartItem item) {
    if (item.quantity > 1) {
      item.quantity -= 1;
    } else {
      cartItems.remove(item);
    }
  }

  void clearCart() {
    cartItems.clear();
  }

  double get totalAmount {
    return cartItems.fold(0, (sum, item) => sum + item.totalPrice);
  }
}

Cart Model Code:

// models/cart_model.dart
class CartItem {
  final String productId;
  final String productName;
  final double price;
  int quantity;

  CartItem({
    required this.productId,
    required this.productName,
    required this.price,
    required this.quantity,
  });

  double get totalPrice => price * quantity;

  void increaseQuantity() {
    quantity += 1;
  }

  void decreaseQuantity() {
    if (quantity > 1) {
      quantity--;
    }
  }
}

Main File Code:

// main.dart
import 'package:exclusive_details_and_restorations/const/cart_controller.dart';
import 'package:exclusive_details_and_restorations/views/about_view.dart';
import 'package:exclusive_details_and_restorations/views/cart_view.dart';
import 'package:exclusive_details_and_restorations/views/gallery_view.dart';
import 'package:exclusive_details_and_restorations/views/home_view.dart';
import 'package:exclusive_details_and_restorations/views/service_areas.dart';
import 'package:exclusive_details_and_restorations/views/services.dart';
import 'package:exclusive_details_and_restorations/views/shop.dart';
import 'package:exclusive_details_and_restorations/views/splash.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      debugShowCheckedModeBanner: false,
      initialBinding: BindingsBuilder(() {
        Get.put(CartController());
      }),
      routes: {
        '/': (context) => const SplashScreen(),
        'home': (context) => const HomeScreen(),
        'about': (context) => const AboutView(),
        'serviceAreas': (context) => const ServiceAreasView(),
        'gallery': (context) => const GalleryView(),
        'services': (context) => const ServicesView(),
        'shop': (context) => EcommerceTest(),
        'cart': (context) => CartScreen(),
      },
    );
  }
}

Solution

  • An RxList only notifies an Obx to rebuild when items are added or removed, not when they are modified. You will need to call refresh() on it to do it manually, like this:

      void addItem(CartItem newItem) {
        int existingIndex = cartItems.indexWhere(
          (item) => item.productName == newItem.productName,
        );
    
        if (existingIndex != -1) {
          cartItems[existingIndex].quantity += 1;
          cartItems.refresh();
        } else {
          cartItems.add(newItem);
        }
      }
    
      void decreaseQuantity(CartItem item) {
        if (item.quantity > 1) {
          item.quantity -= 1;
          cartItems.refresh();
        } else {
          cartItems.remove(item);
        }
      }
    

    Another option is to use a GetBuilder instead of an Obx. The call to cartController.update(); makes that one refresh. Right now your cartController.update(); doesn't have any effect since you don't use any GetBuilder;