├── README.md ├── lib ├── cartmodel.dart ├── cartpage.dart ├── home.dart └── main.dart ├── pubspec.lock ├── pubspec.yaml ├── screenshot ├── cart.jpg └── home.jpg └── shopping.iml /README.md: -------------------------------------------------------------------------------- 1 | # Flutter Shopping Cart 2 | A Shopping Cart (Ecommerce) using Flutter scoped_model 3 | 4 | ### Dependencies 5 | 6 | ``` 7 | scoped_model: ^1.0.1 8 | ``` 9 | 10 | ### Flutter Shopping Cart Features 11 | 12 | - GridView.builder for displaying Products List 13 | - ListView.builder for cart 14 | - Add Product to Cart 15 | - Remove Product from Cart 16 | - Increase & Decrease Product Qty 17 | - Remove Produt when Product Qty == 0 18 | - Calculate Product Price (Qty * price) 19 | - Calculate overall pricing 20 | 21 | 22 | ### Screenshots 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /lib/cartmodel.dart: -------------------------------------------------------------------------------- 1 | import 'package:scoped_model/scoped_model.dart'; 2 | 3 | class CartModel extends Model { 4 | List cart = []; 5 | double totalCartValue = 0; 6 | 7 | int get total => cart.length; 8 | 9 | void addProduct(product) { 10 | int index = cart.indexWhere((i) => i.id == product.id); 11 | print(index); 12 | if (index != -1) 13 | updateProduct(product, product.qty + 1); 14 | else { 15 | cart.add(product); 16 | calculateTotal(); 17 | notifyListeners(); 18 | } 19 | } 20 | 21 | void removeProduct(product) { 22 | int index = cart.indexWhere((i) => i.id == product.id); 23 | cart[index].qty = 1; 24 | cart.removeWhere((item) => item.id == product.id); 25 | calculateTotal(); 26 | notifyListeners(); 27 | } 28 | 29 | void updateProduct(product, qty) { 30 | int index = cart.indexWhere((i) => i.id == product.id); 31 | cart[index].qty = qty; 32 | if (cart[index].qty == 0) 33 | removeProduct(product); 34 | 35 | calculateTotal(); 36 | notifyListeners(); 37 | } 38 | 39 | void clearCart() { 40 | cart.forEach((f) => f.qty = 1); 41 | cart = []; 42 | notifyListeners(); 43 | } 44 | 45 | void calculateTotal() { 46 | totalCartValue = 0; 47 | cart.forEach((f) { 48 | totalCartValue += f.price * f.qty; 49 | }); 50 | } 51 | } 52 | 53 | class Product { 54 | int id; 55 | String title; 56 | String imgUrl; 57 | double price; 58 | int qty; 59 | 60 | Product({this.id, this.title, this.price, this.qty, this.imgUrl}); 61 | } 62 | -------------------------------------------------------------------------------- /lib/cartpage.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:scoped_model/scoped_model.dart'; 3 | import 'package:shopping/cartmodel.dart'; 4 | 5 | class CartPage extends StatefulWidget { 6 | @override 7 | State createState() { 8 | return _CartPageState(); 9 | } 10 | } 11 | 12 | class _CartPageState extends State { 13 | @override 14 | Widget build(BuildContext context) { 15 | // TODO: implement build 16 | return Scaffold( 17 | appBar: AppBar( 18 | backgroundColor: Colors.indigo, 19 | title: Text("Cart"), 20 | actions: [ 21 | FlatButton( 22 | child: Text( 23 | "Clear", 24 | style: TextStyle(color: Colors.white), 25 | ), 26 | onPressed: () => ScopedModel.of(context).clearCart()) 27 | ], 28 | ), 29 | body: ScopedModel.of(context, rebuildOnChange: true) 30 | .cart 31 | .length == 32 | 0 33 | ? Center( 34 | child: Text("No items in Cart"), 35 | ) 36 | : Container( 37 | padding: EdgeInsets.all(8.0), 38 | child: Column(children: [ 39 | Expanded( 40 | child: ListView.builder( 41 | itemCount: ScopedModel.of(context, 42 | rebuildOnChange: true) 43 | .total, 44 | itemBuilder: (context, index) { 45 | return ScopedModelDescendant( 46 | builder: (context, child, model) { 47 | return ListTile( 48 | title: Text(model.cart[index].title), 49 | subtitle: Text(model.cart[index].qty.toString() + 50 | " x " + 51 | model.cart[index].price.toString() + 52 | " = " + 53 | (model.cart[index].qty * 54 | model.cart[index].price) 55 | .toString()), 56 | trailing: Row( 57 | mainAxisSize: MainAxisSize.min, 58 | children: [ 59 | IconButton( 60 | icon: Icon(Icons.add), 61 | onPressed: () { 62 | model.updateProduct(model.cart[index], 63 | model.cart[index].qty + 1); 64 | // model.removeProduct(model.cart[index]); 65 | }, 66 | ), 67 | IconButton( 68 | icon: Icon(Icons.remove), 69 | onPressed: () { 70 | model.updateProduct(model.cart[index], 71 | model.cart[index].qty - 1); 72 | // model.removeProduct(model.cart[index]); 73 | }, 74 | ), 75 | ]), 76 | ); 77 | }, 78 | ); 79 | }, 80 | ), 81 | ), 82 | Container( 83 | padding: EdgeInsets.all(8.0), 84 | child: Text( 85 | "Total: \$ " + 86 | ScopedModel.of(context, 87 | rebuildOnChange: true) 88 | .totalCartValue 89 | .toString() + 90 | "", 91 | style: TextStyle( 92 | fontSize: 24.0, fontWeight: FontWeight.bold), 93 | )), 94 | SizedBox( 95 | width: double.infinity, 96 | child: RaisedButton( 97 | color: Colors.yellow[900], 98 | textColor: Colors.white, 99 | elevation: 0, 100 | child: Text("BUY NOW"), 101 | onPressed: () {}, 102 | )) 103 | ]))); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /lib/home.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:scoped_model/scoped_model.dart'; 3 | import 'package:shopping/cartmodel.dart'; 4 | 5 | class HomePage extends StatelessWidget { 6 | List _products = [ 7 | Product( 8 | id: 1, 9 | title: "Apple", 10 | price: 20.0, 11 | imgUrl: "https://img.icons8.com/plasticine/2x/apple.png", 12 | qty: 1), 13 | Product( 14 | id: 2, 15 | title: "Banana", 16 | price: 40.0, 17 | imgUrl: "https://img.icons8.com/cotton/2x/banana.png", 18 | qty: 1), 19 | Product( 20 | id: 3, 21 | title: "Orange", 22 | price: 20.0, 23 | imgUrl: "https://img.icons8.com/cotton/2x/orange.png", 24 | qty: 1), 25 | Product( 26 | id: 4, 27 | title: "Melon", 28 | price: 40.0, 29 | imgUrl: "https://img.icons8.com/cotton/2x/watermelon.png", 30 | qty: 1), 31 | Product( 32 | id: 5, 33 | title: "Avocado", 34 | price: 25.0, 35 | imgUrl: "https://img.icons8.com/cotton/2x/avocado.png", 36 | qty: 1), 37 | ]; 38 | 39 | @override 40 | Widget build(BuildContext context) { 41 | return Scaffold( 42 | backgroundColor: Colors.indigo[50], 43 | appBar: AppBar( 44 | backgroundColor: Colors.indigo, 45 | title: Text("Home"), 46 | actions: [ 47 | IconButton( 48 | icon: Icon(Icons.shopping_cart), 49 | onPressed: () => Navigator.pushNamed(context, '/cart'), 50 | ) 51 | ], 52 | ), 53 | body: 54 | GridView.builder( 55 | padding: EdgeInsets.all(8.0), 56 | itemCount: _products.length, 57 | gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, mainAxisSpacing: 8, crossAxisSpacing: 8, childAspectRatio: 0.8), 58 | itemBuilder: (context, index){ 59 | return ScopedModelDescendant( 60 | builder: (context, child, model) { 61 | return Card( child: Column( children: [ 62 | Image.network(_products[index].imgUrl, height: 120, width: 120,), 63 | Text(_products[index].title, style: TextStyle(fontWeight: FontWeight.bold),), 64 | Text("\$"+_products[index].price.toString()), 65 | OutlineButton( 66 | child: Text("Add"), 67 | onPressed: () => model.addProduct(_products[index])) 68 | ])); 69 | }); 70 | }, 71 | ), 72 | 73 | // ListView.builder( 74 | // itemExtent: 80, 75 | // itemCount: _products.length, 76 | // itemBuilder: (context, index) { 77 | // return ScopedModelDescendant( 78 | // builder: (context, child, model) { 79 | // return ListTile( 80 | // leading: Image.network(_products[index].imgUrl), 81 | // title: Text(_products[index].title), 82 | // subtitle: Text("\$"+_products[index].price.toString()), 83 | // trailing: OutlineButton( 84 | // child: Text("Add"), 85 | // onPressed: () => model.addProduct(_products[index]))); 86 | // }); 87 | // }, 88 | // ), 89 | 90 | ); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:scoped_model/scoped_model.dart'; 3 | import 'package:shopping/cartmodel.dart'; 4 | import 'package:shopping/cartpage.dart'; 5 | import 'package:shopping/home.dart'; 6 | import 'package:shopping/main.dart'; 7 | 8 | void main() => runApp(MyApp( 9 | model: CartModel(), 10 | )); 11 | 12 | 13 | class MyApp extends StatelessWidget{ 14 | 15 | final CartModel model; 16 | 17 | const MyApp({Key key, @required this.model}) : super(key: key); 18 | 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | // TODO: implement build 23 | return ScopedModel( 24 | model: model, 25 | child: MaterialApp( 26 | debugShowCheckedModeBanner: false, 27 | title: 'Shopping Cart', 28 | home: HomePage(), 29 | routes: {'/cart': (context) => CartPage()}, 30 | ), 31 | ); 32 | } 33 | } -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://www.dartlang.org/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "2.1.0" 11 | boolean_selector: 12 | dependency: transitive 13 | description: 14 | name: boolean_selector 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "1.0.4" 18 | charcode: 19 | dependency: transitive 20 | description: 21 | name: charcode 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "1.1.2" 25 | collection: 26 | dependency: transitive 27 | description: 28 | name: collection 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "1.14.11" 32 | cupertino_icons: 33 | dependency: "direct main" 34 | description: 35 | name: cupertino_icons 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "0.1.2" 39 | flutter: 40 | dependency: "direct main" 41 | description: flutter 42 | source: sdk 43 | version: "0.0.0" 44 | flutter_test: 45 | dependency: "direct dev" 46 | description: flutter 47 | source: sdk 48 | version: "0.0.0" 49 | matcher: 50 | dependency: transitive 51 | description: 52 | name: matcher 53 | url: "https://pub.dartlang.org" 54 | source: hosted 55 | version: "0.12.5" 56 | meta: 57 | dependency: transitive 58 | description: 59 | name: meta 60 | url: "https://pub.dartlang.org" 61 | source: hosted 62 | version: "1.1.6" 63 | path: 64 | dependency: transitive 65 | description: 66 | name: path 67 | url: "https://pub.dartlang.org" 68 | source: hosted 69 | version: "1.6.2" 70 | pedantic: 71 | dependency: transitive 72 | description: 73 | name: pedantic 74 | url: "https://pub.dartlang.org" 75 | source: hosted 76 | version: "1.5.0" 77 | quiver: 78 | dependency: transitive 79 | description: 80 | name: quiver 81 | url: "https://pub.dartlang.org" 82 | source: hosted 83 | version: "2.0.2" 84 | scoped_model: 85 | dependency: "direct main" 86 | description: 87 | name: scoped_model 88 | url: "https://pub.dartlang.org" 89 | source: hosted 90 | version: "1.0.1" 91 | sky_engine: 92 | dependency: transitive 93 | description: flutter 94 | source: sdk 95 | version: "0.0.99" 96 | source_span: 97 | dependency: transitive 98 | description: 99 | name: source_span 100 | url: "https://pub.dartlang.org" 101 | source: hosted 102 | version: "1.5.5" 103 | stack_trace: 104 | dependency: transitive 105 | description: 106 | name: stack_trace 107 | url: "https://pub.dartlang.org" 108 | source: hosted 109 | version: "1.9.3" 110 | stream_channel: 111 | dependency: transitive 112 | description: 113 | name: stream_channel 114 | url: "https://pub.dartlang.org" 115 | source: hosted 116 | version: "2.0.0" 117 | string_scanner: 118 | dependency: transitive 119 | description: 120 | name: string_scanner 121 | url: "https://pub.dartlang.org" 122 | source: hosted 123 | version: "1.0.4" 124 | term_glyph: 125 | dependency: transitive 126 | description: 127 | name: term_glyph 128 | url: "https://pub.dartlang.org" 129 | source: hosted 130 | version: "1.1.0" 131 | test_api: 132 | dependency: transitive 133 | description: 134 | name: test_api 135 | url: "https://pub.dartlang.org" 136 | source: hosted 137 | version: "0.2.4" 138 | typed_data: 139 | dependency: transitive 140 | description: 141 | name: typed_data 142 | url: "https://pub.dartlang.org" 143 | source: hosted 144 | version: "1.1.6" 145 | vector_math: 146 | dependency: transitive 147 | description: 148 | name: vector_math 149 | url: "https://pub.dartlang.org" 150 | source: hosted 151 | version: "2.0.8" 152 | sdks: 153 | dart: ">=2.2.0 <3.0.0" 154 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: shopping 2 | description: A new Flutter project. 3 | 4 | # The following defines the version and build number for your application. 5 | # A version number is three numbers separated by dots, like 1.2.43 6 | # followed by an optional build number separated by a +. 7 | # Both the version and the builder number may be overridden in flutter 8 | # build by specifying --build-name and --build-number, respectively. 9 | # In Android, build-name is used as versionName while build-number used as versionCode. 10 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 11 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 12 | # Read more about iOS versioning at 13 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 14 | version: 1.0.0+1 15 | 16 | environment: 17 | sdk: ">=2.1.0 <3.0.0" 18 | 19 | dependencies: 20 | flutter: 21 | sdk: flutter 22 | scoped_model: ^1.0.1 23 | 24 | # The following adds the Cupertino Icons font to your application. 25 | # Use with the CupertinoIcons class for iOS style icons. 26 | cupertino_icons: ^0.1.2 27 | 28 | dev_dependencies: 29 | flutter_test: 30 | sdk: flutter 31 | 32 | 33 | # For information on the generic Dart part of this file, see the 34 | # following page: https://www.dartlang.org/tools/pub/pubspec 35 | 36 | # The following section is specific to Flutter. 37 | flutter: 38 | 39 | # The following line ensures that the Material Icons font is 40 | # included with your application, so that you can use the icons in 41 | # the material Icons class. 42 | uses-material-design: true 43 | 44 | # To add assets to your application, add an assets section, like this: 45 | # assets: 46 | # - images/a_dot_burr.jpeg 47 | # - images/a_dot_ham.jpeg 48 | 49 | # An image asset can refer to one or more resolution-specific "variants", see 50 | # https://flutter.dev/assets-and-images/#resolution-aware. 51 | 52 | # For details regarding adding assets from package dependencies, see 53 | # https://flutter.dev/assets-and-images/#from-packages 54 | 55 | # To add custom fonts to your application, add a fonts section here, 56 | # in this "flutter" section. Each entry in this list should have a 57 | # "family" key with the font family name, and a "fonts" key with a 58 | # list giving the asset and other descriptors for the font. For 59 | # example: 60 | # fonts: 61 | # - family: Schyler 62 | # fonts: 63 | # - asset: fonts/Schyler-Regular.ttf 64 | # - asset: fonts/Schyler-Italic.ttf 65 | # style: italic 66 | # - family: Trajan Pro 67 | # fonts: 68 | # - asset: fonts/TrajanPro.ttf 69 | # - asset: fonts/TrajanPro_Bold.ttf 70 | # weight: 700 71 | # 72 | # For details regarding fonts from package dependencies, 73 | # see https://flutter.dev/custom-fonts/#from-packages 74 | -------------------------------------------------------------------------------- /screenshot/cart.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codesundar/flutter-shopping-cart/9e3ed7360f190b6397159178fa45badfaa6985de/screenshot/cart.jpg -------------------------------------------------------------------------------- /screenshot/home.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codesundar/flutter-shopping-cart/9e3ed7360f190b6397159178fa45badfaa6985de/screenshot/home.jpg -------------------------------------------------------------------------------- /shopping.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | --------------------------------------------------------------------------------