├── .flutter-plugins ├── .flutter-plugins-dependencies ├── .gitignore ├── LICENSE ├── README.md ├── analysis_options.yaml ├── assets ├── cart.json └── products_list.json ├── lib ├── controller │ ├── cart_controller.dart │ └── services │ │ ├── boxes.dart │ │ └── cart_controller.dart ├── flutter_cart.dart ├── model │ ├── cart_model.dart │ └── cart_model.g.dart ├── res │ └── components │ │ ├── loading_widget.dart │ │ └── round_button.dart └── widgets │ ├── cart_list.dart │ └── checkout_button_widget.dart ├── pubspec.yaml └── test └── flutter_cart_test.dart /.flutter-plugins: -------------------------------------------------------------------------------- 1 | # This is a generated file; do not edit or check into version control. 2 | path_provider=/Users/asiftaj/.pub-cache/hosted/pub.dev/path_provider-2.1.2/ 3 | path_provider_android=/Users/asiftaj/.pub-cache/hosted/pub.dev/path_provider_android-2.2.2/ 4 | path_provider_foundation=/Users/asiftaj/.pub-cache/hosted/pub.dev/path_provider_foundation-2.3.2/ 5 | path_provider_linux=/Users/asiftaj/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/ 6 | path_provider_windows=/Users/asiftaj/.pub-cache/hosted/pub.dev/path_provider_windows-2.2.1/ 7 | -------------------------------------------------------------------------------- /.flutter-plugins-dependencies: -------------------------------------------------------------------------------- 1 | {"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"path_provider_foundation","path":"/Users/asiftaj/.pub-cache/hosted/pub.dev/path_provider_foundation-2.3.2/","shared_darwin_source":true,"native_build":true,"dependencies":[]}],"android":[{"name":"path_provider_android","path":"/Users/asiftaj/.pub-cache/hosted/pub.dev/path_provider_android-2.2.2/","native_build":true,"dependencies":[]}],"macos":[{"name":"path_provider_foundation","path":"/Users/asiftaj/.pub-cache/hosted/pub.dev/path_provider_foundation-2.3.2/","shared_darwin_source":true,"native_build":true,"dependencies":[]}],"linux":[{"name":"path_provider_linux","path":"/Users/asiftaj/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/","native_build":false,"dependencies":[]}],"windows":[{"name":"path_provider_windows","path":"/Users/asiftaj/.pub-cache/hosted/pub.dev/path_provider_windows-2.2.1/","native_build":false,"dependencies":[]}],"web":[]},"dependencyGraph":[{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]}],"date_created":"2023-01-30 22:11:29.581923","version":"3.16.5"} 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. 26 | /pubspec.lock 27 | **/doc/api/ 28 | .dart_tool/ 29 | build/ 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Dean Leung 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FlutterCart: Your Mobile Shopping Companion 2 | 3 | FlutterCart is a versatile Flutter package that brings robust shopping cart capabilities to your mobile application. It leverages Hive for local storage, ensuring your shopping cart data is retained across different app sessions. 4 | 5 | ## Key Features 6 | 7 | - **Initialization**: Kickstart the shopping cart with the `init()` method. 8 | - **Cart Operations**: Include items to the cart with `addToCart`, and remove them using `removeFromCart`. 9 | - **Quantity Control**: Fine-tune the item quantity with `incrementCartItemQuantity` and `decrementCartItemQuantity`. 10 | - **Total Price Calculation**: Fetch the total price of cart items using `calculateTotalPrice`. 11 | - **Item Count**: Retrieve the total number of items with `getCartItemCount`. 12 | - **Cart Clearance**: Wipe out all items from the cart with `clearCart`. 13 | - **Cart Item Display**: Visualize the cart items with `showCartItems`, offering customizable widgets for individual cart items and an empty cart message. 14 | - **Item Count Widget**: Display a widget featuring the current cart item count using `showCartItemCountWidget`. 15 | - **Total Amount Widget**: Showcase a widget revealing the total amount of items in the cart with `showTotalAmountWidget`. 16 | - **Dynamic Cart Item Widget**: Illustrate a widget that updates based on whether a product is in the cart or not, using `showAndUpdateCartItemWidget`. 17 | - **Cart Data Retrieval**: Use `getCartData` in the `FlutterCart` class to fetch a list of cart items and the total price. 18 | 19 | ## How to Get Started 20 | 21 | 1. Incorporate the package in your Dart file: 22 | 23 | ```dart 24 | import 'package:flutter_cart/flutter_cart.dart'; 25 | ``` 26 | 27 | 2. Set the ball rolling by initializing the cart: 28 | 29 | ```dart 30 | await FlutterCart().init(); 31 | ``` 32 | 33 | 3. You're all set to harness the shopping cart features in your application! 34 | 35 | ## Usage Examples 36 | 37 | ```dart 38 | // Add products to the cart 39 | await FlutterCart().addToCart(FlutterCartItem()); 40 | 41 | // Eliminate a product from the cart 42 | await FlutterCart().removeFromCart(productId); 43 | 44 | // Increase the product quantity in the cart 45 | await FlutterCart().incrementCartItemQuantity(productId); 46 | 47 | // Reduce the product quantity in the cart 48 | await FlutterCart().decrementCartItemQuantity(productId); 49 | 50 | // Fetch the total price of cart items 51 | double totalPrice = FlutterCart().calculateTotalPrice(); 52 | 53 | // Get the total quantity of cart items 54 | int itemCount = FlutterCart().getCartItemCount(); 55 | 56 | // Empty the cart 57 | FlutterCart().clearCart(); 58 | 59 | // Fetch cart data and total price 60 | Map cartData = FlutterCart().getCartData(); 61 | List cartItems = cartData['cartItems']; 62 | double totalPriceFromData = cartData['totalPrice']; 63 | ``` 64 | 65 | ## Widgets 66 | 67 | ### Cart Item Display 68 | 69 | ```dart 70 | FlutterCart().showCartItems( 71 | cartTileWidget: ({required FlutterCartItem data}) { 72 | // Your personalized cart item widget 73 | }, 74 | showEmptyCartMsgWidget: YourEmptyCartMessageWidget(), 75 | ); 76 | ``` 77 | 78 | ### Cart Item Count Widget 79 | 80 | ```dart 81 | FlutterCart().showCartItemCountWidget( 82 | cartItemCountWidgetBuilder: (int itemCount) { 83 | // Your personalized widget displaying the cart item count 84 | }, 85 | ); 86 | ``` 87 | 88 | ### Total Amount Widget 89 | 90 | ```dart 91 | FlutterCart().showTotalAmountWidget( 92 | cartTotalAmountWidgetBuilder: (double totalAmount) { 93 | // Your personalized widget displaying the total amount 94 | }, 95 | ); 96 | ``` 97 | 98 | ### Dynamic Cart Item Widget 99 | 100 | ```dart 101 | FlutterCart().showAndUpdateCartItemWidget( 102 | inCartWidget: YourInCartWidget(), 103 | notInCartWidget: YourNotInCartWidget(), 104 | product: yourProduct, 105 | ); 106 | ``` 107 | 108 | "Simplify your mobile shopping experience with FlutterCart!" 109 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:flutter_lints/flutter.yaml 2 | 3 | # Additional information about this file can be found at 4 | # https://dart.dev/guides/language/analysis-options 5 | -------------------------------------------------------------------------------- /assets/cart.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "Cart", 3 | "id": "27b39077-aa57-48a5-b504-914f68fa44dc", 4 | "version": 1, 5 | "createdAt": "2023-01-23T13:06:28.569Z", 6 | "lastModifiedAt": "2023-01-23T13:06:28.569Z", 7 | "lastModifiedBy": { 8 | "isPlatformClient": false, 9 | "userId": "user123", 10 | "username": "john_doe" 11 | }, 12 | "createdBy": { 13 | "isPlatformClient": false, 14 | "userId": "user456", 15 | "username": "jane_smith" 16 | }, 17 | "lineItems": [], 18 | "cartState": "Active", 19 | "totalPrice": { 20 | "type": "centPrecision", 21 | "currencyCode": "US", 22 | "centAmount": 0, 23 | "fractionDigits": 2, 24 | "tax": {"type": "percentage", "percentage": 10} 25 | }, 26 | "shippingMode": "Multiple", 27 | "itemShippingAddresses": [ 28 | { 29 | "itemId": "item123", 30 | "address": { 31 | "street": "123 Main St", 32 | "city": "Cityville", 33 | "postalCode": "12345", 34 | "country": "Countryland" 35 | } 36 | }, 37 | { 38 | "itemId": "item456", 39 | "address": { 40 | "street": "789 Oak St", 41 | "city": "Townsville", 42 | "postalCode": "67890", 43 | "country": "Countryland" 44 | } 45 | }, 46 | { 47 | "itemId": "item789", 48 | "address": { 49 | "street": "456 Pine St", 50 | "city": "Villageton", 51 | "postalCode": "56789", 52 | "country": "Countryland" 53 | } 54 | } 55 | ], 56 | "inventoryMode": "None", 57 | "taxMode": "Platform", 58 | "taxRoundingMode": "HalfEven", 59 | "taxCalculationMode": "LineItemLevel", 60 | "origin": "Customer" 61 | } 62 | -------------------------------------------------------------------------------- /assets/products_list.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "1", 4 | "productId": "1", 5 | "quantity": 2, 6 | "unitPrice": { 7 | "type": "centPrecision", 8 | "currencyCode": "US", 9 | "centAmount": 1000, 10 | "fractionDigits": 2 11 | }, 12 | "totalPrice": { 13 | "type": "centPrecision", 14 | "currencyCode": "US", 15 | "centAmount": 1000, 16 | "fractionDigits": 2 17 | }, 18 | "discounts": [ 19 | { 20 | "code": "DISCOUNT123", 21 | "amount": { 22 | "type": "centPrecision", 23 | "currencyCode": "US", 24 | "centAmount": 500, 25 | "fractionDigits": 2 26 | } 27 | } 28 | ], 29 | "imageUrls": [ 30 | "https://cdn.dummyjson.com/product-images/1/1.jpg", 31 | "https://cdn.dummyjson.com/product-images/1/2.jpg", 32 | "https://cdn.dummyjson.com/product-images/1/3.jpg", 33 | "https://cdn.dummyjson.com/product-images/1/4.jpg", 34 | "https://cdn.dummyjson.com/product-images/1/thumbnail.jpg" 35 | ], 36 | "title": "iPhone 9", 37 | "description": "An apple mobile which is nothing like apple", 38 | "rating": 4.69, 39 | "stock": 94, 40 | "brand": "Apple", 41 | "category": "smartphones", 42 | "thumbnail": "https://cdn.dummyjson.com/product-images/1/thumbnail.jpg" 43 | }, 44 | { 45 | "id": "2", 46 | "productId": "2", 47 | "quantity": 1, 48 | "unitPrice": { 49 | "type": "centPrecision", 50 | "currencyCode": "US", 51 | "centAmount": 2000, 52 | "fractionDigits": 2 53 | }, 54 | "totalPrice": { 55 | "type": "centPrecision", 56 | "currencyCode": "US", 57 | "centAmount": 2000, 58 | "fractionDigits": 2 59 | }, 60 | "discounts": [], 61 | "imageUrls": [ 62 | "https://cdn.dummyjson.com/product-images/2/1.jpg", 63 | "https://cdn.dummyjson.com/product-images/2/2.jpg", 64 | "https://cdn.dummyjson.com/product-images/2/3.jpg", 65 | "https://cdn.dummyjson.com/product-images/2/thumbnail.jpg" 66 | ], 67 | "title": "iPhone X", 68 | "description": "SIM-Free, Model A19211 6.5-inch Super Retina HD display with OLED technology A12 Bionic chip with ...", 69 | "rating": 4.44, 70 | "stock": 34, 71 | "brand": "Apple", 72 | "category": "smartphones", 73 | "thumbnail": "https://cdn.dummyjson.com/product-images/2/thumbnail.jpg" 74 | }, 75 | { 76 | "id": "3", 77 | "productId": "3", 78 | "quantity": 3, 79 | "unitPrice": { 80 | "type": "centPrecision", 81 | "currencyCode": "US", 82 | "centAmount": 3000, 83 | "fractionDigits": 2 84 | }, 85 | "totalPrice": { 86 | "type": "centPrecision", 87 | "currencyCode": "US", 88 | "centAmount": 3000, 89 | "fractionDigits": 2 90 | }, 91 | "discounts": [ 92 | { 93 | "code": "DISCOUNT456", 94 | "amount": { 95 | "type": "centPrecision", 96 | "currencyCode": "US", 97 | "centAmount": 200, 98 | "fractionDigits": 2 99 | } 100 | } 101 | ], 102 | "imageUrls": [ 103 | "https://images.pexels.com/photos/2529148/pexels-photo-2529148.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2", 104 | "https://images.pexels.com/photos/2529148/pexels-photo-2529148.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2" 105 | ], 106 | "title": "iPhone 9", 107 | "description": "An apple mobile which is nothing like apple", 108 | "rating": 4.69, 109 | "stock": 94, 110 | "brand": "Apple", 111 | "category": "smartphones", 112 | "thumbnail": "https://cdn.dummyjson.com/product-images/1/thumbnail.jpg" 113 | }, 114 | { 115 | "id": "4", 116 | "productId": "4", 117 | "quantity": 3, 118 | "unitPrice": { 119 | "type": "centPrecision", 120 | "currencyCode": "US", 121 | "centAmount": 3000, 122 | "fractionDigits": 2 123 | }, 124 | "totalPrice": { 125 | "type": "centPrecision", 126 | "currencyCode": "US", 127 | "centAmount": 3000, 128 | "fractionDigits": 2 129 | }, 130 | "discounts": [ 131 | { 132 | "code": "DISCOUNT456", 133 | "amount": { 134 | "type": "centPrecision", 135 | "currencyCode": "US", 136 | "centAmount": 200, 137 | "fractionDigits": 2 138 | } 139 | } 140 | ], 141 | "imageUrls": [ 142 | "https://cdn.dummyjson.com/product-images/2/3.jpg" 143 | ], 144 | "title": "iPhone 9", 145 | "description": "An apple mobile which is nothing like apple", 146 | "rating": 4.69, 147 | "stock": 94, 148 | "brand": "Apple", 149 | "category": "smartphones", 150 | "thumbnail": "https://cdn.dummyjson.com/product-images/2/3.jpg" 151 | }, 152 | { 153 | "id": "5", 154 | "productId": "5", 155 | "quantity": 3, 156 | "unitPrice": { 157 | "type": "centPrecision", 158 | "currencyCode": "US", 159 | "centAmount": 3000, 160 | "fractionDigits": 2 161 | }, 162 | "totalPrice": { 163 | "type": "centPrecision", 164 | "currencyCode": "US", 165 | "centAmount": 3000, 166 | "fractionDigits": 2 167 | }, 168 | "discounts": [ 169 | { 170 | "code": "DISCOUNT456", 171 | "amount": { 172 | "type": "centPrecision", 173 | "currencyCode": "US", 174 | "centAmount": 200, 175 | "fractionDigits": 2 176 | } 177 | } 178 | ], 179 | "imageUrls": [ 180 | "https://cdn.dummyjson.com/product-images/2/2.jpg" 181 | ], 182 | "title": "iPhone 9", 183 | "description": "An apple mobile which is nothing like apple", 184 | "rating": 4.69, 185 | "stock": 94, 186 | "brand": "Apple", 187 | "category": "smartphones", 188 | "thumbnail": "https://cdn.dummyjson.com/product-images/2/2.jpg" 189 | }, 190 | { 191 | "id": "6", 192 | "productId": "6", 193 | "quantity": 3, 194 | "unitPrice": { 195 | "type": "centPrecision", 196 | "currencyCode": "US", 197 | "centAmount": 3000, 198 | "fractionDigits": 2 199 | }, 200 | "totalPrice": { 201 | "type": "centPrecision", 202 | "currencyCode": "US", 203 | "centAmount": 3000, 204 | "fractionDigits": 2 205 | }, 206 | "discounts": [ 207 | { 208 | "code": "DISCOUNT456", 209 | "amount": { 210 | "type": "centPrecision", 211 | "currencyCode": "US", 212 | "centAmount": 200, 213 | "fractionDigits": 2 214 | } 215 | } 216 | ], 217 | "imageUrls": [ 218 | "https://example.com/item789_1.jpg", 219 | "https://example.com/item789_2.jpg", 220 | "https://example.com/item789_3.jpg" 221 | ], 222 | "title": "iPhone 9", 223 | "description": "An apple mobile which is nothing like apple", 224 | "rating": 4.69, 225 | "stock": 94, 226 | "brand": "Apple", 227 | "category": "smartphones", 228 | "thumbnail": "https://cdn.dummyjson.com/product-images/1/thumbnail.jpg" 229 | }, 230 | { 231 | "id": "7", 232 | "productId": "7", 233 | "quantity": 3, 234 | "unitPrice": { 235 | "type": "centPrecision", 236 | "currencyCode": "US", 237 | "centAmount": 3000, 238 | "fractionDigits": 2 239 | }, 240 | "totalPrice": { 241 | "type": "centPrecision", 242 | "currencyCode": "US", 243 | "centAmount": 3000, 244 | "fractionDigits": 2 245 | }, 246 | "discounts": [ 247 | { 248 | "code": "DISCOUNT456", 249 | "amount": { 250 | "type": "centPrecision", 251 | "currencyCode": "US", 252 | "centAmount": 200, 253 | "fractionDigits": 2 254 | } 255 | } 256 | ], 257 | "imageUrls": [ 258 | "https://example.com/item789_1.jpg", 259 | "https://example.com/item789_2.jpg", 260 | "https://example.com/item789_3.jpg" 261 | ], 262 | "title": "iPhone 9", 263 | "description": "An apple mobile which is nothing like apple", 264 | "rating": 4.69, 265 | "stock": 94, 266 | "brand": "Apple", 267 | "category": "smartphones", 268 | "thumbnail": "https://cdn.dummyjson.com/product-images/1/thumbnail.jpg" 269 | }, 270 | { 271 | "id": "8", 272 | "productId": "8", 273 | "quantity": 3, 274 | "unitPrice": { 275 | "type": "centPrecision", 276 | "currencyCode": "US", 277 | "centAmount": 3000, 278 | "fractionDigits": 2 279 | }, 280 | "totalPrice": { 281 | "type": "centPrecision", 282 | "currencyCode": "US", 283 | "centAmount": 3000, 284 | "fractionDigits": 2 285 | }, 286 | "discounts": [ 287 | { 288 | "code": "DISCOUNT456", 289 | "amount": { 290 | "type": "centPrecision", 291 | "currencyCode": "US", 292 | "centAmount": 200, 293 | "fractionDigits": 2 294 | } 295 | } 296 | ], 297 | "imageUrls": [ 298 | "https://example.com/item789_1.jpg", 299 | "https://example.com/item789_2.jpg", 300 | "https://example.com/item789_3.jpg" 301 | ], 302 | "title": "iPhone 9", 303 | "description": "An apple mobile which is nothing like apple", 304 | "rating": 4.69, 305 | "stock": 94, 306 | "brand": "Apple", 307 | "category": "smartphones", 308 | "thumbnail": "https://cdn.dummyjson.com/product-images/1/thumbnail.jpg" 309 | }, 310 | { 311 | "id": "9", 312 | "productId": "9", 313 | "quantity": 3, 314 | "unitPrice": { 315 | "type": "centPrecision", 316 | "currencyCode": "US", 317 | "centAmount": 3000, 318 | "fractionDigits": 2 319 | }, 320 | "totalPrice": { 321 | "type": "centPrecision", 322 | "currencyCode": "US", 323 | "centAmount": 3000, 324 | "fractionDigits": 2 325 | }, 326 | "discounts": [ 327 | { 328 | "code": "DISCOUNT456", 329 | "amount": { 330 | "type": "centPrecision", 331 | "currencyCode": "US", 332 | "centAmount": 200, 333 | "fractionDigits": 2 334 | } 335 | } 336 | ], 337 | "imageUrls": [ 338 | "https://example.com/item789_1.jpg", 339 | "https://example.com/item789_2.jpg", 340 | "https://example.com/item789_3.jpg" 341 | ], 342 | "title": "iPhone 9", 343 | "description": "An apple mobile which is nothing like apple", 344 | "rating": 4.69, 345 | "stock": 94, 346 | "brand": "Apple", 347 | "category": "smartphones", 348 | "thumbnail": "https://cdn.dummyjson.com/product-images/1/thumbnail.jpg" 349 | }, 350 | { 351 | "id": "10", 352 | "productId": "10", 353 | "quantity": 3, 354 | "unitPrice": { 355 | "type": "centPrecision", 356 | "currencyCode": "US", 357 | "centAmount": 3000, 358 | "fractionDigits": 2 359 | }, 360 | "totalPrice": { 361 | "type": "centPrecision", 362 | "currencyCode": "US", 363 | "centAmount": 3000, 364 | "fractionDigits": 2 365 | }, 366 | "discounts": [ 367 | { 368 | "code": "DISCOUNT456", 369 | "amount": { 370 | "type": "centPrecision", 371 | "currencyCode": "US", 372 | "centAmount": 200, 373 | "fractionDigits": 2 374 | } 375 | } 376 | ], 377 | "imageUrls": [ 378 | "https://example.com/item789_1.jpg", 379 | "https://example.com/item789_2.jpg", 380 | "https://example.com/item789_3.jpg" 381 | ], 382 | "title": "iPhone 9", 383 | "description": "An apple mobile which is nothing like apple", 384 | "rating": 4.69, 385 | "stock": 94, 386 | "brand": "Apple", 387 | "category": "smartphones", 388 | "thumbnail": "https://cdn.dummyjson.com/product-images/1/thumbnail.jpg" 389 | }, 390 | { 391 | "id": "11", 392 | "productId": "11", 393 | "quantity": 3, 394 | "unitPrice": { 395 | "type": "centPrecision", 396 | "currencyCode": "US", 397 | "centAmount": 3000, 398 | "fractionDigits": 2 399 | }, 400 | "totalPrice": { 401 | "type": "centPrecision", 402 | "currencyCode": "US", 403 | "centAmount": 3000, 404 | "fractionDigits": 2 405 | }, 406 | "discounts": [ 407 | { 408 | "code": "DISCOUNT456", 409 | "amount": { 410 | "type": "centPrecision", 411 | "currencyCode": "US", 412 | "centAmount": 200, 413 | "fractionDigits": 2 414 | } 415 | } 416 | ], 417 | "imageUrls": [ 418 | "https://example.com/item789_1.jpg", 419 | "https://example.com/item789_2.jpg", 420 | "https://example.com/item789_3.jpg" 421 | ], 422 | "title": "iPhone 9", 423 | "description": "An apple mobile which is nothing like apple", 424 | "rating": 4.69, 425 | "stock": 94, 426 | "brand": "Apple", 427 | "category": "smartphones", 428 | "thumbnail": "https://cdn.dummyjson.com/product-images/1/thumbnail.jpg" 429 | }, 430 | { 431 | "id": "12", 432 | "productId": "12", 433 | "quantity": 3, 434 | "unitPrice": { 435 | "type": "centPrecision", 436 | "currencyCode": "US", 437 | "centAmount": 3000, 438 | "fractionDigits": 2 439 | }, 440 | "totalPrice": { 441 | "type": "centPrecision", 442 | "currencyCode": "US", 443 | "centAmount": 3000, 444 | "fractionDigits": 2 445 | }, 446 | "discounts": [ 447 | { 448 | "code": "DISCOUNT456", 449 | "amount": { 450 | "type": "centPrecision", 451 | "currencyCode": "US", 452 | "centAmount": 200, 453 | "fractionDigits": 2 454 | } 455 | } 456 | ], 457 | "imageUrls": [ 458 | "https://example.com/item789_1.jpg", 459 | "https://example.com/item789_2.jpg", 460 | "https://example.com/item789_3.jpg" 461 | ], 462 | "title": "iPhone 9", 463 | "description": "An apple mobile which is nothing like apple", 464 | "rating": 4.69, 465 | "stock": 94, 466 | "brand": "Apple", 467 | "category": "smartphones", 468 | "thumbnail": "https://cdn.dummyjson.com/product-images/1/thumbnail.jpg" 469 | }, 470 | { 471 | "id": "13", 472 | "productId": "13", 473 | "quantity": 3, 474 | "unitPrice": { 475 | "type": "centPrecision", 476 | "currencyCode": "US", 477 | "centAmount": 3000, 478 | "fractionDigits": 2 479 | }, 480 | "totalPrice": { 481 | "type": "centPrecision", 482 | "currencyCode": "US", 483 | "centAmount": 3000, 484 | "fractionDigits": 2 485 | }, 486 | "discounts": [ 487 | { 488 | "code": "DISCOUNT456", 489 | "amount": { 490 | "type": "centPrecision", 491 | "currencyCode": "US", 492 | "centAmount": 200, 493 | "fractionDigits": 2 494 | } 495 | } 496 | ], 497 | "imageUrls": [ 498 | "https://example.com/item789_1.jpg", 499 | "https://example.com/item789_2.jpg", 500 | "https://example.com/item789_3.jpg" 501 | ], 502 | "title": "iPhone 9", 503 | "description": "An apple mobile which is nothing like apple", 504 | "rating": 4.69, 505 | "stock": 94, 506 | "brand": "Apple", 507 | "category": "smartphones", 508 | "thumbnail": "https://cdn.dummyjson.com/product-images/1/thumbnail.jpg" 509 | }, 510 | { 511 | "id": "14", 512 | "productId": "14", 513 | "quantity": 3, 514 | "unitPrice": { 515 | "type": "centPrecision", 516 | "currencyCode": "US", 517 | "centAmount": 3000, 518 | "fractionDigits": 2 519 | }, 520 | "totalPrice": { 521 | "type": "centPrecision", 522 | "currencyCode": "US", 523 | "centAmount": 3000, 524 | "fractionDigits": 2 525 | }, 526 | "discounts": [ 527 | { 528 | "code": "DISCOUNT456", 529 | "amount": { 530 | "type": "centPrecision", 531 | "currencyCode": "US", 532 | "centAmount": 200, 533 | "fractionDigits": 2 534 | } 535 | } 536 | ], 537 | "imageUrls": [ 538 | "https://example.com/item789_1.jpg", 539 | "https://example.com/item789_2.jpg", 540 | "https://example.com/item789_3.jpg" 541 | ], 542 | "title": "iPhone 9", 543 | "description": "An apple mobile which is nothing like apple", 544 | "rating": 4.69, 545 | "stock": 94, 546 | "brand": "Apple", 547 | "category": "smartphones", 548 | "thumbnail": "https://cdn.dummyjson.com/product-images/1/thumbnail.jpg" 549 | }, 550 | { 551 | "id": "15", 552 | "productId": "15", 553 | "quantity": 3, 554 | "unitPrice": { 555 | "type": "centPrecision", 556 | "currencyCode": "US", 557 | "centAmount": 3000, 558 | "fractionDigits": 2 559 | }, 560 | "totalPrice": { 561 | "type": "centPrecision", 562 | "currencyCode": "US", 563 | "centAmount": 3000, 564 | "fractionDigits": 2 565 | }, 566 | "discounts": [ 567 | { 568 | "code": "DISCOUNT456", 569 | "amount": { 570 | "type": "centPrecision", 571 | "currencyCode": "US", 572 | "centAmount": 200, 573 | "fractionDigits": 2 574 | } 575 | } 576 | ], 577 | "imageUrls": [ 578 | "https://example.com/item789_1.jpg", 579 | "https://example.com/item789_2.jpg", 580 | "https://example.com/item789_3.jpg" 581 | ], 582 | "title": "iPhone 9", 583 | "description": "An apple mobile which is nothing like apple", 584 | "rating": 4.69, 585 | "stock": 94, 586 | "brand": "Apple", 587 | "category": "smartphones", 588 | "thumbnail": "https://cdn.dummyjson.com/product-images/1/thumbnail.jpg" 589 | }, 590 | { 591 | "id": "16", 592 | "productId": "16", 593 | "quantity": 3, 594 | "unitPrice": { 595 | "type": "centPrecision", 596 | "currencyCode": "US", 597 | "centAmount": 3000, 598 | "fractionDigits": 2 599 | }, 600 | "totalPrice": { 601 | "type": "centPrecision", 602 | "currencyCode": "US", 603 | "centAmount": 3000, 604 | "fractionDigits": 2 605 | }, 606 | "discounts": [ 607 | { 608 | "code": "DISCOUNT456", 609 | "amount": { 610 | "type": "centPrecision", 611 | "currencyCode": "US", 612 | "centAmount": 200, 613 | "fractionDigits": 2 614 | } 615 | } 616 | ], 617 | "imageUrls": [ 618 | "https://example.com/item789_1.jpg", 619 | "https://example.com/item789_2.jpg", 620 | "https://example.com/item789_3.jpg" 621 | ], 622 | "title": "iPhone 9", 623 | "description": "An apple mobile which is nothing like apple", 624 | "rating": 4.69, 625 | "stock": 94, 626 | "brand": "Apple", 627 | "category": "smartphones", 628 | "thumbnail": "https://cdn.dummyjson.com/product-images/1/thumbnail.jpg" 629 | }, 630 | { 631 | "id": "17", 632 | "productId": "17", 633 | "quantity": 3, 634 | "unitPrice": { 635 | "type": "centPrecision", 636 | "currencyCode": "US", 637 | "centAmount": 3000, 638 | "fractionDigits": 2 639 | }, 640 | "totalPrice": { 641 | "type": "centPrecision", 642 | "currencyCode": "US", 643 | "centAmount": 3000, 644 | "fractionDigits": 2 645 | }, 646 | "discounts": [ 647 | { 648 | "code": "DISCOUNT456", 649 | "amount": { 650 | "type": "centPrecision", 651 | "currencyCode": "US", 652 | "centAmount": 200, 653 | "fractionDigits": 2 654 | } 655 | } 656 | ], 657 | "imageUrls": [ 658 | "https://example.com/item789_1.jpg", 659 | "https://example.com/item789_2.jpg", 660 | "https://example.com/item789_3.jpg" 661 | ], 662 | "title": "iPhone 9", 663 | "description": "An apple mobile which is nothing like apple", 664 | "rating": 4.69, 665 | "stock": 94, 666 | "brand": "Apple", 667 | "category": "smartphones", 668 | "thumbnail": "https://cdn.dummyjson.com/product-images/1/thumbnail.jpg" 669 | }, 670 | { 671 | "id": "18", 672 | "productId": "18", 673 | "quantity": 3, 674 | "unitPrice": { 675 | "type": "centPrecision", 676 | "currencyCode": "US", 677 | "centAmount": 3000, 678 | "fractionDigits": 2 679 | }, 680 | "totalPrice": { 681 | "type": "centPrecision", 682 | "currencyCode": "US", 683 | "centAmount": 3000, 684 | "fractionDigits": 2 685 | }, 686 | "discounts": [ 687 | { 688 | "code": "DISCOUNT456", 689 | "amount": { 690 | "type": "centPrecision", 691 | "currencyCode": "US", 692 | "centAmount": 200, 693 | "fractionDigits": 2 694 | } 695 | } 696 | ], 697 | "imageUrls": [ 698 | "https://example.com/item789_1.jpg", 699 | "https://example.com/item789_2.jpg", 700 | "https://example.com/item789_3.jpg" 701 | ], 702 | "title": "iPhone 9", 703 | "description": "An apple mobile which is nothing like apple", 704 | "rating": 4.69, 705 | "stock": 94, 706 | "brand": "Apple", 707 | "category": "smartphones", 708 | "thumbnail": "https://cdn.dummyjson.com/product-images/1/thumbnail.jpg" 709 | } 710 | ] 711 | -------------------------------------------------------------------------------- /lib/controller/cart_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:hive_flutter/hive_flutter.dart'; 3 | import 'package:flutter_cart/model/cart_model.dart'; 4 | 5 | /// Manages the shopping cart functionality, including adding, removing, 6 | /// and updating items in the cart. 7 | class CartController { 8 | /// The Hive box used to store cart items. 9 | final Box _cartBox = 10 | Hive.box('cartBox'); 11 | 12 | /// A [ValueListenable] for the cart box, allowing widgets to listen for changes in the cart. 13 | ValueListenable> get cartListenable => 14 | _cartBox.listenable(); 15 | 16 | /// Adds a [FlutterCartItem] to the shopping cart. 17 | /// 18 | /// If the product is already in the cart, it updates the quantity. If not, it adds a new instance. 19 | void addToCart(FlutterCartItem item) { 20 | // Retrieve the existing item using the product ID as the key 21 | FlutterCartItem? existingItem = _cartBox.get(item.productId); 22 | if (existingItem != null) { 23 | // Product is already in the cart, update the quantity 24 | existingItem.quantity = item.quantity; // Resetting the quantity:) 25 | _cartBox.put( 26 | existingItem.key, 27 | FlutterCartItem( 28 | productId: existingItem.productId, 29 | productDescription: existingItem.productDescription, 30 | quantity: 1, 31 | productName: existingItem.productName, 32 | productDetails: existingItem.productDetails, 33 | productImages: existingItem.productImages, 34 | unitPrice: existingItem.unitPrice, 35 | productThumbnail: existingItem.productThumbnail, 36 | ), 37 | ); 38 | } else { 39 | // Product is not in the cart, add a new instance 40 | _cartBox.put( 41 | item.key, 42 | FlutterCartItem( 43 | productId: item.productId, 44 | quantity: item.quantity, 45 | productName: item.productName, 46 | unitPrice: item.unitPrice, 47 | productDescription: item.productDescription, 48 | productDetails: item.productDetails, 49 | productImages: item.productImages, 50 | productThumbnail: item.productThumbnail, 51 | )); 52 | } 53 | } 54 | 55 | /// Removes a product from the shopping cart. 56 | /// 57 | /// Returns `true` if the product is successfully removed, `false` if the product is not found in the cart. 58 | bool removeFromCart(String productId) { 59 | if (_cartBox.keys.contains(productId)) { 60 | _cartBox.delete(productId); 61 | return true; // Product successfully removed 62 | } else { 63 | return false; // Product not found in the cart 64 | } 65 | } 66 | 67 | /// Increments the quantity of a cart item. 68 | void incrementQuantity(String productId) { 69 | FlutterCartItem? item = _cartBox.get(productId); 70 | if (item != null) { 71 | item.increment(); 72 | _cartBox.put(item.key, item); 73 | } 74 | } 75 | 76 | /// Decrements the quantity of a cart item. 77 | /// 78 | /// Optionally, removes the item from the cart if the quantity becomes 1. 79 | void decrementQuantity(String productId) { 80 | FlutterCartItem? item = _cartBox.get(productId); 81 | if (item != null) { 82 | if (item.quantity > 1) { 83 | item.decrement(); 84 | _cartBox.put(item.key, item); 85 | } else { 86 | // Optionally, we can remove the item from the cart if the quantity is 1 87 | // removeProduct(productId); 88 | } 89 | } 90 | } 91 | 92 | /// Calculates the total price of all items in the shopping cart. 93 | double calculateTotalPrice() { 94 | double total = 0; 95 | for (var i = 0; i < _cartBox.length; i++) { 96 | total += _cartBox.getAt(i)!.totalPrice; 97 | } 98 | return total; 99 | } 100 | 101 | /// Gets the total number of items in the shopping cart. 102 | int getCartItemCount() { 103 | return _cartBox.length; 104 | } 105 | 106 | /// Checks if a product with the given [productId] exists in the shopping cart. 107 | bool isItemExistsInCart(String productId) { 108 | return _cartBox.keys.contains(productId); 109 | } 110 | 111 | /// Removes a product from the cart using the [productId]. 112 | /// 113 | /// Returns `true` if the product is successfully removed, `false` if the product is not found in the cart. 114 | bool removeProduct(String productId) { 115 | if (_cartBox.keys.contains(productId)) { 116 | _cartBox.delete(productId); 117 | return true; // Product successfully removed 118 | } else { 119 | return false; // Product not found in the cart 120 | } 121 | } 122 | 123 | /// Gets the total price of all items in the shopping cart. 124 | double getTotalPrice() { 125 | var box = Hive.box('cartBox'); 126 | 127 | double totalAmount = 0; 128 | 129 | // Iterate through each item in the cart and calculate the total price 130 | for (var i = 0; i < box.length; i++) { 131 | FlutterCartItem item = box.getAt(i)!; 132 | totalAmount += item.totalPrice; 133 | } 134 | 135 | return totalAmount; 136 | } 137 | 138 | /// Retrieves a list of [FlutterCartItem] from the shopping cart. 139 | List getCartItems() { 140 | List cartItems = []; 141 | 142 | for (var i = 0; i < _cartBox.length; i++) { 143 | FlutterCartItem item = _cartBox.getAt(i)!; 144 | cartItems.add(item); 145 | } 146 | 147 | return cartItems; 148 | } 149 | 150 | /// Clears all items from the shopping cart. 151 | void clearCart() { 152 | _cartBox.clear(); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /lib/controller/services/boxes.dart: -------------------------------------------------------------------------------- 1 | import 'package:hive/hive.dart'; 2 | import 'package:flutter_cart/model/cart_model.dart'; 3 | 4 | /// A utility class providing access to the Hive box for storing cart data. 5 | class Boxes { 6 | /// Gets the Hive box for storing [FlutterCartItem] data. 7 | /// 8 | /// Returns a [Box] instance for interacting with the cart data stored in Hive. 9 | static Box getData() => 10 | Hive.box('cartBox'); 11 | } 12 | -------------------------------------------------------------------------------- /lib/controller/services/cart_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:hive_flutter/hive_flutter.dart'; 3 | import 'package:flutter_cart/controller/services/boxes.dart'; 4 | import 'package:flutter_cart/model/cart_model.dart'; 5 | 6 | class CartController { 7 | final Box _cartBox = Boxes.getData(); 8 | 9 | ValueListenable> get cartListenable => _cartBox.listenable(); 10 | 11 | Future updateCart(LineItems product) async { 12 | CartModel? existingCart; 13 | 14 | var box = Hive.box('cartBox'); 15 | 16 | for (var cart in box.values) { 17 | if (cart.lineItems!.any((item) => item.productId == product.productId)) { 18 | existingCart = cart; 19 | break; 20 | } 21 | } 22 | 23 | // If the item is in the cart, remove it 24 | if (existingCart != null) { 25 | existingCart.lineItems! 26 | .removeWhere((item) => item.productId == product.productId); 27 | await box.put(existingCart.key, existingCart); 28 | print('Item removed from cart: ${product.productId}'); 29 | } else { 30 | // If the item is not in the cart, add it 31 | CartModel cartModel = box.isNotEmpty ? box.getAt(0)! : CartModel(); 32 | cartModel.lineItems ??= []; 33 | cartModel.lineItems!.add(LineItems.fromJson(product.toJson())); 34 | await box.put(0, cartModel); 35 | print('Item added to cart: ${product.productId}'); 36 | } 37 | await updateTotalPrice(); 38 | } 39 | 40 | bool isItemExistsInCart(String productId) { 41 | var box = Hive.box('cartBox'); 42 | 43 | for (var cart in box.values) { 44 | if (cart.lineItems!.any((item) => item.productId == productId)) { 45 | return true; 46 | } 47 | } 48 | return false; 49 | } 50 | 51 | int getCartItemCount() { 52 | var cart = Hive.box('cartBox').isNotEmpty 53 | ? Hive.box('cartBox').getAt(0) 54 | : null; 55 | return cart?.lineItems?.length ?? 0; 56 | } 57 | 58 | // decrement product quantity in cart 59 | void decrementQuantity(String productId) { 60 | _updateQuantity(productId, -1); 61 | updateTotalPrice(); 62 | } 63 | 64 | void incrementQuantity(String productId) { 65 | _updateQuantity(productId, 1); 66 | // After updating the quantity, call updateTotalPrice 67 | updateTotalPrice(); 68 | } 69 | 70 | void _updateQuantity(String productId, int change) { 71 | var box = Hive.box('cartBox'); 72 | for (var cart in box.values) { 73 | for (var item in cart.lineItems!) { 74 | if (item.productId == productId) { 75 | item.quantity = (item.quantity ?? 0) + change; 76 | 77 | // Ensure the quantity doesn't go below 0 78 | item.quantity = item.quantity! < 0 ? 0 : item.quantity; 79 | 80 | box.put(cart.key, cart); 81 | print('Quantity updated for $productId: ${item.quantity}'); 82 | return; 83 | } 84 | } 85 | } 86 | } 87 | 88 | // Function to remove a product from the cart by its product ID 89 | Future removeProduct(String productId) async { 90 | var box = Hive.box('cartBox'); 91 | for (var cart in box.values) { 92 | for (var item in cart.lineItems!) { 93 | if (item.productId == productId) { 94 | cart.lineItems!.remove(item); 95 | box.put(cart.key, cart); 96 | print('Product removed from cart: $productId'); 97 | // After removing the product, call updateTotalPrice 98 | await updateTotalPrice(); 99 | return true; // Indicate that removal was successful 100 | } 101 | } 102 | } 103 | 104 | return false; // Indicate that the product was not found in the cart 105 | } 106 | 107 | // Function to calculate and update the total price in the cart 108 | Future updateTotalPrice() async { 109 | var box = Hive.box('cartBox'); 110 | 111 | // Retrieve the CartModel from Hive 112 | CartModel? cartModel = box.isNotEmpty ? box.getAt(0) : null; 113 | 114 | if (cartModel != null) { 115 | // Calculate the total price 116 | TotalPrice totalPriceObject = calculateTotalPrice(cartModel.lineItems); 117 | 118 | // Update the CartModel with the calculated total price 119 | cartModel.totalPrice = totalPriceObject; 120 | 121 | // Put the updated CartModel back into Hive 122 | await box.put(0, cartModel); 123 | 124 | print('Total price updated: ${totalPriceObject.centAmount}'); 125 | } 126 | } 127 | 128 | // Function to calculate total price based on line items 129 | TotalPrice calculateTotalPrice(List? lineItems) { 130 | int totalAmount = 0; 131 | 132 | if (lineItems != null) { 133 | for (var item in lineItems) { 134 | int itemTotalAmount = 135 | (item.quantity ?? 0) * (item.totalPrice?.centAmount ?? 0); 136 | totalAmount += itemTotalAmount; 137 | } 138 | } 139 | 140 | // Dummy tax calculation 141 | int taxAmount = (totalAmount * 0.0).round(); // 10% tax, adjust accordingly 142 | 143 | // Creating a totalPrice object with calculated values 144 | TotalPrice totalPriceObject = TotalPrice( 145 | type: 'dummy', 146 | currencyCode: 'USD', 147 | centAmount: totalAmount + taxAmount, 148 | fractionDigits: 2, 149 | ); 150 | 151 | return totalPriceObject; 152 | } 153 | 154 | // Function to get the total price from the cart 155 | TotalPrice? getTotalPrice() { 156 | var box = Hive.box('cartBox'); 157 | 158 | // Retrieve the CartModel from Hive 159 | CartModel? cartModel = box.isNotEmpty ? box.getAt(0) : null; 160 | 161 | // Return the total price from the CartModel 162 | return cartModel?.totalPrice; 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /lib/flutter_cart.dart: -------------------------------------------------------------------------------- 1 | /// The main library for the fluttercart package. 2 | /// Provides functionality to manage a shopping cart using Hive for local storage. 3 | library flutter_cart; 4 | 5 | import 'dart:developer'; 6 | import 'package:flutter/material.dart'; 7 | import 'package:hive_flutter/hive_flutter.dart'; 8 | import 'package:path_provider/path_provider.dart'; 9 | import 'package:flutter_cart/controller/cart_controller.dart'; 10 | import 'package:flutter_cart/model/cart_model.dart'; 11 | import 'package:flutter_cart/widgets/cart_list.dart'; 12 | 13 | // Extension method for Iterable class 14 | extension IterableExtensions on Iterable { 15 | T? firstWhereOrNull(bool Function(T) test) { 16 | for (final element in this) { 17 | if (test(element)) { 18 | return element; 19 | } 20 | } 21 | return null; 22 | } 23 | } 24 | 25 | /// The main class for interacting with the fluttercart. 26 | class FlutterCart { 27 | /// Initializes Hive and opens the cart box. 28 | Future init() async { 29 | WidgetsFlutterBinding.ensureInitialized(); 30 | var directory = await getApplicationDocumentsDirectory(); 31 | Hive.init(directory.path); 32 | // Register the adapters 33 | Hive.registerAdapter(FlutterCartItemAdapter()); 34 | //open cart box 35 | await Hive.openBox('cartBox'); 36 | } 37 | 38 | /// Adds a [FlutterCartItem] to the shopping cart. 39 | Future addToCart(FlutterCartItem cartItem) async { 40 | CartController().addToCart(cartItem); 41 | log('CartItem added to Hive box: ${cartItem.toJson()}'); 42 | } 43 | 44 | /// Removes a product from the shopping cart. 45 | Future removeFromCart(String productId) async { 46 | bool removed = CartController().removeFromCart(productId); 47 | if (removed) { 48 | log('CartItem removed from Hive box: $productId'); 49 | } else { 50 | log('Product not found in the cart: $productId'); 51 | } 52 | return removed; 53 | } 54 | 55 | /// Increments the quantity of a cart item. 56 | Future incrementCartItemQuantity(String productId) async { 57 | CartController().incrementQuantity(productId); 58 | log('CartItem quantity incremented: $productId'); 59 | } 60 | 61 | /// Decrements the quantity of a cart item. 62 | Future decrementCartItemQuantity(String productId) async { 63 | CartController().decrementQuantity(productId); 64 | log('CartItem quantity decremented: $productId'); 65 | } 66 | 67 | /// Calculates the total price of all items in the shopping cart. 68 | double calculateTotalPrice() { 69 | return CartController().calculateTotalPrice(); 70 | } 71 | 72 | /// Gets the total number of items in the shopping cart. 73 | int getCartItemCount() { 74 | return CartController().getCartItemCount(); 75 | } 76 | 77 | /// Clears all items from the shopping cart. 78 | void clearCart() { 79 | CartController().clearCart(); 80 | } 81 | 82 | /// Retrieves the cart data including items and total price. 83 | Map getCartData() { 84 | final cartController = CartController(); 85 | 86 | // Get the list of cart items 87 | List cartItems = cartController.getCartItems(); 88 | 89 | // Get the total price 90 | double totalPrice = cartController.calculateTotalPrice(); 91 | 92 | // Return a map with cart data and total price 93 | return { 94 | 'cartItems': cartItems, 95 | 'totalPrice': totalPrice, 96 | }; 97 | } 98 | 99 | /// Displays the list of cart items using the provided widgets. 100 | Widget showCartItems({ 101 | required Widget Function({required FlutterCartItem data}) 102 | cartTileWidget, 103 | required Widget showEmptyCartMsgWidget, 104 | }) { 105 | return ListCartItems( 106 | cartTileWidget: cartTileWidget, 107 | showEmptyCartMsgWidget: showEmptyCartMsgWidget, 108 | ); 109 | } 110 | 111 | /// Displays the current cart item count using the provided widget builder. 112 | Widget showCartItemCountWidget( 113 | {required Widget Function(int itemCount) cartItemCountWidgetBuilder}) { 114 | return ValueListenableBuilder>( 115 | valueListenable: CartController().cartListenable, 116 | builder: (context, box, child) { 117 | var itemCount = CartController().getCartItemCount(); 118 | return cartItemCountWidgetBuilder(itemCount); 119 | }, 120 | ); 121 | } 122 | 123 | /// Displays the total amount in the cart using the provided widget builder. 124 | Widget showTotalAmountWidget( 125 | {required Widget Function(double totalAmount) 126 | cartTotalAmountWidgetBuilder}) { 127 | return ValueListenableBuilder>( 128 | valueListenable: CartController().cartListenable, 129 | builder: (context, box, child) { 130 | double totalAmount = CartController().getTotalPrice(); 131 | return cartTotalAmountWidgetBuilder(totalAmount); 132 | }, 133 | ); 134 | } 135 | 136 | /// Displays an icon button based on whether a product is in the cart or not. 137 | Widget showAndUpdateCartItemWidget({ 138 | required Widget inCartWidget, 139 | required Widget notInCartWidget, 140 | required FlutterCartItem product, 141 | }) { 142 | return ValueListenableBuilder>( 143 | valueListenable: CartController().cartListenable, 144 | builder: (context, box, child) { 145 | bool existsInCart = 146 | CartController().isItemExistsInCart(product.productId); 147 | 148 | return IconButton( 149 | onPressed: () async { 150 | existsInCart 151 | ? await removeFromCart(product.productId) 152 | : await addToCart(product); 153 | }, 154 | icon: existsInCart ? inCartWidget : notInCartWidget, 155 | ); 156 | }, 157 | ); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /lib/model/cart_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:hive/hive.dart'; 2 | part 'cart_model.g.dart'; // Generated by Hive 3 | 4 | /// Represents an item in the shopping cart with easy storage capabilities. 5 | /// 6 | /// This class is annotated with Hive type and fields annotations for serialization. 7 | @HiveType(typeId: 0) 8 | class FlutterCartItem { 9 | /// The unique identifier for the product. 10 | @HiveField(0) 11 | final String productId; 12 | 13 | /// The name of the product. 14 | @HiveField(1) 15 | final String productName; 16 | 17 | /// The optional description of the product. 18 | @HiveField(2) 19 | final String? productDescription; 20 | 21 | /// The optional thumbnail image URL for the product. 22 | @HiveField(3) 23 | final String? productThumbnail; 24 | 25 | /// The optional list of image URLs for the product. 26 | @HiveField(4) 27 | final List? productImages; 28 | 29 | /// The unit price of the product. 30 | @HiveField(5) 31 | final double unitPrice; 32 | 33 | /// The quantity of the product in the shopping cart. 34 | @HiveField(6) 35 | int quantity; 36 | 37 | /// The optional details of the product. 38 | @HiveField(7) 39 | final Map? productDetails; 40 | 41 | /// Getter for the key, using the [productId] as the key. 42 | @HiveField(8) 43 | String get key => productId; 44 | 45 | /// Creates a [FlutterCartItem] instance. 46 | /// 47 | /// The [productId], [productName], [unitPrice], and [quantity] are required. 48 | FlutterCartItem({ 49 | required this.productId, 50 | required this.productName, 51 | this.productThumbnail, 52 | this.productDescription, 53 | this.productImages, 54 | required this.unitPrice, 55 | required this.quantity, 56 | this.productDetails, 57 | }); 58 | 59 | /// Converts the [FlutterCartItem] instance to a Map. 60 | /// 61 | /// This method is used for serialization. 62 | Map toJson() { 63 | return { 64 | 'productId': productId, 65 | 'productName': productName, 66 | 'productDescription': productDescription, 67 | 'productThumbnail': productThumbnail, 68 | 'productImages': productImages, 69 | 'unitPrice': unitPrice, 70 | 'quantity': quantity, 71 | 'productDetails': productDetails, 72 | }; 73 | } 74 | 75 | /// Calculates the total price for this item. 76 | double get totalPrice => unitPrice * quantity; 77 | 78 | /// Increments the quantity of this item. 79 | void increment() { 80 | quantity++; 81 | } 82 | 83 | /// Decrements the quantity of this item, ensuring it doesn't go below zero. 84 | void decrement() { 85 | if (quantity > 0) { 86 | quantity--; 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /lib/model/cart_model.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'cart_model.dart'; 4 | 5 | // ************************************************************************** 6 | // TypeAdapterGenerator 7 | // ************************************************************************** 8 | 9 | class FlutterCartItemAdapter 10 | extends TypeAdapter { 11 | @override 12 | final int typeId = 0; 13 | 14 | @override 15 | FlutterCartItem read(BinaryReader reader) { 16 | final numOfFields = reader.readByte(); 17 | final fields = { 18 | for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), 19 | }; 20 | return FlutterCartItem( 21 | productId: fields[0] as String, 22 | productName: fields[1] as String, 23 | productThumbnail: fields[3] as String?, 24 | productDescription: fields[2] as String?, 25 | productImages: (fields[4] as List?)?.cast(), 26 | unitPrice: fields[5] as double, 27 | quantity: fields[6] as int, 28 | productDetails: (fields[7] as Map?)?.cast(), 29 | ); 30 | } 31 | 32 | @override 33 | void write(BinaryWriter writer, FlutterCartItem obj) { 34 | writer 35 | ..writeByte(9) 36 | ..writeByte(0) 37 | ..write(obj.productId) 38 | ..writeByte(1) 39 | ..write(obj.productName) 40 | ..writeByte(2) 41 | ..write(obj.productDescription) 42 | ..writeByte(3) 43 | ..write(obj.productThumbnail) 44 | ..writeByte(4) 45 | ..write(obj.productImages) 46 | ..writeByte(5) 47 | ..write(obj.unitPrice) 48 | ..writeByte(6) 49 | ..write(obj.quantity) 50 | ..writeByte(7) 51 | ..write(obj.productDetails) 52 | ..writeByte(8) 53 | ..write(obj.key); 54 | } 55 | 56 | @override 57 | int get hashCode => typeId.hashCode; 58 | 59 | @override 60 | bool operator ==(Object other) => 61 | identical(this, other) || 62 | other is FlutterCartItemAdapter && 63 | runtimeType == other.runtimeType && 64 | typeId == other.typeId; 65 | } 66 | -------------------------------------------------------------------------------- /lib/res/components/loading_widget.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io' show Platform; 2 | import 'package:flutter/cupertino.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | class LoadingWidget extends StatelessWidget { 6 | final double size; 7 | 8 | const LoadingWidget({Key? key, this.size = 36.0}) : super(key: key); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return Center( 13 | child: SizedBox( 14 | width: size, 15 | height: size, 16 | child: Platform.isIOS 17 | ? const CupertinoActivityIndicator( 18 | color: Colors.blue, 19 | ) 20 | : const CircularProgressIndicator( 21 | strokeWidth: 2.0, 22 | color: Colors.blue, 23 | ), 24 | ), 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/res/components/round_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_cart/res/components/loading_widget.dart'; 3 | 4 | class RoundButton extends StatelessWidget { 5 | final String title; 6 | final VoidCallback onPress; 7 | final Color color, textColor, buttonColor; 8 | final bool loading; 9 | final double height; 10 | final double borderRadius; 11 | const RoundButton( 12 | {Key? key, 13 | required this.title, 14 | required this.onPress, 15 | this.height = 50.0, 16 | this.buttonColor = Colors.black, 17 | this.textColor = Colors.white, 18 | this.color = Colors.white, 19 | this.borderRadius = 12.0, 20 | this.loading = false}) 21 | : super(key: key); 22 | 23 | @override 24 | Widget build(BuildContext context) { 25 | return InkWell( 26 | onTap: loading ? null : onPress, 27 | child: Container( 28 | height: height, 29 | width: double.infinity, 30 | decoration: BoxDecoration( 31 | color: buttonColor, 32 | borderRadius: BorderRadius.circular(borderRadius)), 33 | child: loading 34 | ? const LoadingWidget() 35 | : Center( 36 | child: Text( 37 | title, 38 | style: Theme.of(context) 39 | .textTheme 40 | .displayMedium! 41 | .copyWith(fontSize: 16, color: textColor), 42 | )), 43 | ), 44 | ); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/widgets/cart_list.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | import 'package:hive_flutter/hive_flutter.dart'; 3 | import 'package:flutter_cart/controller/cart_controller.dart'; 4 | import 'package:flutter_cart/model/cart_model.dart'; 5 | 6 | /// A widget that displays a list of cart items using a provided tile widget. 7 | /// 8 | /// This widget listens to changes in the cart and updates the UI accordingly. 9 | class ListCartItems extends StatelessWidget { 10 | /// The widget builder function for each cart item. 11 | final Widget Function({required FlutterCartItem data}) 12 | cartTileWidget; 13 | 14 | /// The widget to display when the cart is empty. 15 | final Widget showEmptyCartMsgWidget; 16 | 17 | /// Creates a [ListCartItems] instance. 18 | /// 19 | /// The [cartTileWidget] is a builder function for each cart item, and [showEmptyCartMsgWidget] is displayed when the cart is empty. 20 | const ListCartItems({ 21 | Key? key, 22 | required this.cartTileWidget, 23 | required this.showEmptyCartMsgWidget, 24 | }) : super(key: key); 25 | 26 | @override 27 | Widget build(BuildContext context) { 28 | return ValueListenableBuilder>( 29 | valueListenable: CartController().cartListenable, 30 | builder: (context, box, child) { 31 | if (box.isNotEmpty) { 32 | return ListView.builder( 33 | itemCount: box.length, 34 | itemBuilder: (context, cartIndex) { 35 | var cartItem = box.getAt(cartIndex); 36 | if (cartItem != null) { 37 | return Column( 38 | children: [ 39 | cartTileWidget(data: cartItem), 40 | ], 41 | ); 42 | } else { 43 | return showEmptyCartMsgWidget; 44 | } 45 | }, 46 | ); 47 | } else { 48 | return showEmptyCartMsgWidget; 49 | } 50 | }, 51 | ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/widgets/checkout_button_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:hive_flutter/hive_flutter.dart'; 3 | import 'package:flutter_cart/controller/services/cart_controller.dart'; 4 | import 'package:flutter_cart/model/cart_model.dart'; 5 | import 'package:flutter_cart/res/components/round_button.dart'; 6 | 7 | class CheckoutButton extends StatelessWidget { 8 | final VoidCallback onPress; 9 | 10 | CheckoutButton({ 11 | Key? key, 12 | required this.onPress, 13 | }) : super(key: key); 14 | final CartController _cartController = CartController(); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | Size size = MediaQuery.of(context).size; 19 | return Container( 20 | padding: const EdgeInsets.only(right: 20, top: 20, bottom: 40, left: 20), 21 | height: 160, 22 | width: size.width, 23 | color: Colors.grey.shade100, 24 | child: Center( 25 | child: SizedBox( 26 | width: size.width / 1.25, 27 | child: Column( 28 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 29 | children: [ 30 | ValueListenableBuilder>( 31 | valueListenable: _cartController.cartListenable, 32 | builder: (context, box, child) { 33 | var totalAmount = _cartController.getTotalPrice(); 34 | 35 | return Row( 36 | mainAxisAlignment: MainAxisAlignment.end, 37 | children: [ 38 | Text( 39 | 'Total: \$${totalAmount?.centAmount?.toStringAsFixed(2) ?? '0.00'}', 40 | style: Theme.of(context).textTheme.headlineSmall, 41 | ) 42 | ], 43 | ); 44 | }, 45 | ), 46 | RoundButton(onPress: onPress, title: 'Checkout'), 47 | ], 48 | ), 49 | ), 50 | ), 51 | ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_cart 2 | description: "Effortlessly manage and persist shopping cart data in your Flutter app with this comprehensive package. Simplify local storage and enhance the shopping experience for your users." 3 | version: 0.0.7 4 | homepage: https://github.com/falloutone/fluttercart 5 | 6 | environment: 7 | sdk: '>=3.2.3 <4.0.0' 8 | flutter: ">=1.17.0" 9 | 10 | dependencies: 11 | flutter: 12 | sdk: flutter 13 | hive: ^2.2.3 14 | path_provider: ^2.1.2 15 | hive_flutter: ^1.1.0 16 | 17 | dev_dependencies: 18 | build_runner: ^2.4.8 19 | hive_generator: ^2.0.1 20 | flutter_test: 21 | sdk: flutter 22 | flutter_lints: ^2.0.0 23 | 24 | # For information on the generic Dart part of this file, see the 25 | # following page: https://dart.dev/tools/pub/pubspec 26 | 27 | # The following section is specific to Flutter packages. 28 | flutter: 29 | 30 | # To add assets to your package, add an assets section, like this: 31 | # assets: 32 | # - assets/ 33 | # - images/a_dot_ham.jpeg 34 | # 35 | # For details regarding assets in packages, see 36 | # https://flutter.dev/assets-and-images/#from-packages 37 | # 38 | # An image asset can refer to one or more resolution-specific "variants", see 39 | # https://flutter.dev/assets-and-images/#resolution-aware 40 | 41 | # To add custom fonts to your package, add a fonts section here, 42 | # in this "flutter" section. Each entry in this list should have a 43 | # "family" key with the font family name, and a "fonts" key with a 44 | # list giving the asset and other descriptors for the font. For 45 | # example: 46 | # fonts: 47 | # - family: Schyler 48 | # fonts: 49 | # - asset: fonts/Schyler-Regular.ttf 50 | # - asset: fonts/Schyler-Italic.ttf 51 | # style: italic 52 | # - family: Trajan Pro 53 | # fonts: 54 | # - asset: fonts/TrajanPro.ttf 55 | # - asset: fonts/TrajanPro_Bold.ttf 56 | # weight: 700 57 | # 58 | # For details regarding fonts in packages, see 59 | # https://flutter.dev/custom-fonts/#from-packages 60 | -------------------------------------------------------------------------------- /test/flutter_cart_test.dart: -------------------------------------------------------------------------------- 1 | // import 'package:flutter_test/flutter_test.dart'; 2 | 3 | // import 'package:flutter_cart/flutter_cart.dart'; 4 | 5 | // void main() { 6 | // test('adds one to input values', () { 7 | // final calculator = Calculator(); 8 | // expect(calculator.addOne(2), 3); 9 | // expect(calculator.addOne(-7), -6); 10 | // expect(calculator.addOne(0), 1); 11 | // }); 12 | // } 13 | --------------------------------------------------------------------------------