image

01 Jan 2026

9K

35K

Creating a Dynamic Shopping Cart Widget in Flutter

A dynamic shopping cart is a fundamental component of any e-commerce application, allowing users to select and manage products before checkout. Building such a feature in Flutter requires effective state management to ensure the UI accurately reflects the cart's contents. This article will guide you through creating a fully functional, dynamic shopping cart widget using Flutter and the provider package for state management.

Prerequisites

Before diving in, ensure you have a basic understanding of Flutter widgets, state management concepts, and Dart programming.

Core Concepts

  • State Management: We will use the provider package, a widely adopted solution in Flutter, to manage the cart's state efficiently. It simplifies passing data down the widget tree and rebuilding widgets when the data changes.
  • Data Models: Clearly defined data models for Product and CartItem are crucial for structuring our application's data.

Step-by-Step Implementation

1. Project Setup and Dependencies

First, create a new Flutter project and add the provider dependency to your pubspec.yaml file:


dependencies:
  flutter:
    sdk: flutter
  provider: ^6.0.5 # Use the latest stable version

Then, run flutter pub get.

2. Data Models

Define the Product and CartItem classes. CartItem will include the Product and the quantity.


// models/product.dart
class Product {
  final String id;
  final String name;
  final double price;
  final String imageUrl;

  Product({
    required this.id,
    required this.name,
    required this.price,
    required this.imageUrl,
  });
}

// models/cart_item.dart
class CartItem {
  final Product product;
  int quantity;

  CartItem({
    required this.product,
    this.quantity = 1,
  });
}

3. State Management with Provider (CartProvider)

Create a CartProvider class that extends ChangeNotifier. This class will hold the list of cart items and provide methods to add, remove, clear items, and calculate the total.


// providers/cart_provider.dart
import 'package:flutter/foundation.dart';
import '../models/product.dart';
import '../models/cart_item.dart';

class CartProvider with ChangeNotifier {
  final Map<String, CartItem> _items = {};

  Map<String, CartItem> get items {
    return {..._items};
  }

  int get itemCount {
    return _items.length;
  }

  double get totalAmount {
    var total = 0.0;
    _items.forEach((key, cartItem) {
      total += cartItem.product.price * cartItem.quantity;
    });
    return total;
  }

  void addItem(Product product) {
    if (_items.containsKey(product.id)) {
      _items.update(
        product.id,
        (existingCartItem) => CartItem(
          product: existingCartItem.product,
          quantity: existingCartItem.quantity + 1,
        ),
      );
    } else {
      _items.putIfAbsent(
        product.id,
        () => CartItem(
          product: product,
        ),
      );
    }
    notifyListeners();
  }

  void removeItem(String productId) {
    _items.remove(productId);
    notifyListeners();
  }

  void removeSingleItem(String productId) {
    if (!_items.containsKey(productId)) {
      return;
    }
    if (_items[productId]!.quantity > 1) {
      _items.update(
        productId,
        (existingCartItem) => CartItem(
          product: existingCartItem.product,
          quantity: existingCartItem.quantity - 1,
        ),
      );
    } else {
      _items.remove(productId);
    }
    notifyListeners();
  }

  void clearCart() {
    _items.clear();
    notifyListeners();
  }
}

4. Product Listing Screen

This screen will display a list of available products. Each product will have an "Add to Cart" button that interacts with the CartProvider. Note that the Badge widget is available in Flutter 3.0.0 and above. If you are using an older version, you might need to implement a custom badge.


// screens/product_list_screen.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../models/product.dart';
import '../providers/cart_provider.dart';
import 'cart_screen.dart';

class ProductListScreen extends StatelessWidget {
  final List<Product> _products = [
    Product(
      id: 'p1',
      name: 'Laptop',
      price: 1200.0,
      imageUrl: 'https://via.placeholder.com/150/FF5733/FFFFFF?text=Laptop',
    ),
    Product(
      id: 'p2',
      name: 'Smartphone',
      price: 800.0,
      imageUrl: 'https://via.placeholder.com/150/33FF57/FFFFFF?text=Smartphone',
    ),
    Product(
      id: 'p3',
      name: 'Headphones',
      price: 150.0,
      imageUrl: 'https://via.placeholder.com/150/3357FF/FFFFFF?text=Headphones',
    ),
  ];

  ProductListScreen({super.key});

  @override
  Widget build(BuildContext context) {
    final cart = Provider.of<CartProvider>(context, listen: false);

    return Scaffold(
      appBar: AppBar(
        title: const Text('Our Products'),
        actions: [
          Consumer<CartProvider>(
            builder: (_, cartData, child) => Badge( // Badge widget requires Flutter 3.0.0+
              label: Text(cartData.itemCount.toString()),
              child: IconButton(
                icon: const Icon(Icons.shopping_cart),
                onPressed: () {
                  Navigator.of(context).push(
                    MaterialPageRoute(
                      builder: (ctx) => CartScreen(),
                    ),
                  );
                },
              ),
            ),
          ),
        ],
      ),
      body: ListView.builder(
        itemCount: _products.length,
        itemBuilder: (ctx, i) {
          final product = _products[i];
          return Card(
            margin: const EdgeInsets.all(10),
            child: Padding(
              padding: const EdgeInsets.all(8.0),
              child: Column(
                children: [
                  Image.network(
                    product.imageUrl,
                    height: 100,
                    width: 100,
                    fit: BoxFit.cover,
                  ),
                  ListTile(
                    title: Text(product.name),
                    subtitle: Text('\$${product.price.toStringAsFixed(2)}'),
                    trailing: IconButton(
                      icon: const Icon(Icons.add_shopping_cart),
                      onPressed: () {
                        cart.addItem(product);
                        ScaffoldMessenger.of(context).hideCurrentSnackBar();
                        ScaffoldMessenger.of(context).showSnackBar(
                          SnackBar(
                            content: const Text('Added item to cart!'),
                            duration: const Duration(seconds: 2),
                            action: SnackBarAction(
                              label: 'UNDO',
                              onPressed: () {
                                cart.removeSingleItem(product.id);
                              },
                            ),
                          ),
                        );
                      },
                    ),
                  ),
                ],
              ),
            ),
          );
        },
      ),
    );
  }
}

5. Shopping Cart Screen

This screen will display the items currently in the cart, allowing users to remove items and showing the total amount.


// screens/cart_screen.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/cart_provider.dart';

class CartScreen extends StatelessWidget {
  CartScreen({super.key});

  @override
  Widget build(BuildContext context) {
    final cart = Provider.of<CartProvider>(context);

    return Scaffold(
      appBar: AppBar(
        title: const Text('Your Cart'),
      ),
      body: Column(
        children: [
          Card(
            margin: const EdgeInsets.all(15),
            child: Padding(
              padding: const EdgeInsets.all(8),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  const Text(
                    'Total',
                    style: TextStyle(fontSize: 20),
                  ),
                  const Spacer(),
                  Chip(
                    label: Text(
                      '\$${cart.totalAmount.toStringAsFixed(2)}',
                      style: TextStyle(color: Theme.of(context).colorScheme.onPrimary),
                    ),
                    backgroundColor: Theme.of(context).colorScheme.primary,
                  ),
                  TextButton(
                    onPressed: cart.itemCount == 0 ? null : () {
                      // Implement order placement logic here
                      cart.clearCart();
                      ScaffoldMessenger.of(context).showSnackBar(
                          const SnackBar(content: Text('Order Placed!')));
                    },
                    child: const Text('ORDER NOW'),
                  ),
                ],
              ),
            ),
          ),
          const SizedBox(height: 10),
          Expanded(
            child: ListView.builder(
              itemCount: cart.itemCount,
              itemBuilder: (ctx, i) {
                final cartItem = cart.items.values.toList()[i];
                final productId = cart.items.keys.toList()[i];
                return Dismissible(
                  key: ValueKey(cartItem.product.id),
                  background: Container(
                    color: Theme.of(context).colorScheme.error,
                    alignment: Alignment.centerRight,
                    padding: const EdgeInsets.only(right: 20),
                    margin: const EdgeInsets.symmetric(
                      horizontal: 15,
                      vertical: 4,
                    ),
                    child: const Icon(
                      Icons.delete,
                      color: Colors.white,
                      size: 40,
                    ),
                  ),
                  direction: DismissDirection.endToStart,
                  onDismissed: (direction) {
                    cart.removeItem(productId);
                  },
                  confirmDismiss: (direction) {
                    return showDialog(
                      context: context,
                      builder: (ctx) => AlertDialog(
                        title: const Text('Are you sure?'),
                        content: const Text(
                            'Do you want to remove the item from the cart?'),
                        actions: [
                          TextButton(
                            onPressed: () {
                              Navigator.of(ctx).pop(false);
                            },
                            child: const Text('No'),
                          ),
                          TextButton(
                            onPressed: () {
                              Navigator.of(ctx).pop(true);
                            },
                            child: const Text('Yes'),
                          ),
                        ],
                      ),
                    );
                  },
                  child: Card(
                    margin: const EdgeInsets.symmetric(
                      horizontal: 15,
                      vertical: 4,
                    ),
                    child: Padding(
                      padding: const EdgeInsets.all(8),
                      child: ListTile(
                        leading: CircleAvatar(
                          child: Padding(
                            padding: const EdgeInsets.all(5),
                            child: FittedBox(
                              child: Text('\$${cartItem.product.price}'),
                            ),
                          ),
                        ),
                        title: Text(cartItem.product.name),
                        subtitle: Text(
                            'Total: \$${(cartItem.product.price * cartItem.quantity).toStringAsFixed(2)}'),
                        trailing: Text('${cartItem.quantity} x'),
                      ),
                    ),
                  ),
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}

6. Integrating into the Main Application

Finally, wrap your MaterialApp with ChangeNotifierProvider to make the CartProvider available throughout your widget tree.


// main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'providers/cart_provider.dart';
import 'screens/product_list_screen.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (ctx) => CartProvider(),
      child: MaterialApp(
        title: 'Flutter Shopping Cart',
        theme: ThemeData(
          primarySwatch: Colors.blue,
          colorScheme: ColorScheme.fromSwatch(primarySwatch: Colors.blue)
              .copyWith(secondary: Colors.deepOrange, error: Colors.red),
          fontFamily: 'Lato',
        ),
        home: ProductListScreen(),
        // Define routes if you prefer named routes
        // routes: {
        //   '/cart': (ctx) => CartScreen(),
        // },
      ),
    );
  }
}

Conclusion

You have now successfully built a dynamic shopping cart widget in Flutter using the provider package for state management. This setup provides a solid foundation for more complex e-commerce features. You can further enhance this cart by adding features like quantity adjustment directly in the cart, persistent storage, checkout process integration, and more sophisticated animations. Effective state management is key to building responsive and maintainable Flutter applications, and provider offers a clear and efficient way to achieve this.

Related Articles

May 14, 2026

Building a Multi-Event Countdown Timer Widget with Reminders, Notifications, Repeat, and Custom Labels in Flutter

Building a Multi-Event Countdown Timer Widget with Reminders, Notifications, Repeat, and Custom Labels in Flutter Countdown timers are essential in many applic

May 11, 2026

Unleashing Dynamic UIs: Flutter's Animation Prowess

Unleashing Dynamic UIs: Flutter's Animation Prowess for Slide & Scale Effects Flutter's declarative UI framework, combined with its powerful animation capabilit

May 11, 2026

Building a Product Detail Page Widget in Flutter with Related Items, Review Carousel, Promo Badges, and Quick Buy

Building a Product Detail Page Widget in Flutter with Related Items, Review Carousel, Promo Badges, and Quick Buy A well-designed Product Detail Page (PDP) is