From ff29598ec513609b659bd99505668ea18ae4eb8f Mon Sep 17 00:00:00 2001 From: Sergey Elpashev Date: Thu, 2 May 2024 17:02:43 +0300 Subject: [PATCH] Adding items to cart --- lib/components/{card.dart => item_card.dart} | 10 - lib/main.dart | 6 +- lib/pages/basket.dart | 13 +- lib/pages/detail.dart | 117 +++++++--- lib/tools/prefs.dart | 42 ++++ macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.lock | 206 ++++++++++++++++++ pubspec.yaml | 1 + 8 files changed, 354 insertions(+), 43 deletions(-) rename lib/components/{card.dart => item_card.dart} (74%) create mode 100644 lib/tools/prefs.dart diff --git a/lib/components/card.dart b/lib/components/item_card.dart similarity index 74% rename from lib/components/card.dart rename to lib/components/item_card.dart index fd83d48..da8d14a 100644 --- a/lib/components/card.dart +++ b/lib/components/item_card.dart @@ -37,16 +37,6 @@ class ProductCard extends StatelessWidget { Text('\$$price', style: Theme.of(context).textTheme.titleSmall), ], ), - // child: Column( - // mainAxisSize: MainAxisSize.min, - // children: [ - // imagePath, - // const SizedBox(height: 16), - // Text(name, style: Theme.of(context).textTheme.titleLarge), - // const SizedBox(height: 8), - // Text('\$$price', style: Theme.of(context).textTheme.titleSmall), - // ], - // ), ), ), ); diff --git a/lib/main.dart b/lib/main.dart index f76de59..39701f1 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -4,7 +4,7 @@ import 'dart:js_interop_unsafe' as js_util; import 'package:flutter/material.dart'; import 'package:gymlink_module_web/components/app_bar.dart'; -import 'package:gymlink_module_web/components/card.dart'; +import 'package:gymlink_module_web/components/item_card.dart'; import 'package:gymlink_module_web/pages/basket.dart'; import 'package:gymlink_module_web/pages/detail.dart'; import 'package:gymlink_module_web/pages/order_history.dart'; @@ -147,7 +147,9 @@ class _MainPageState extends State { onPressed: _goToPage, style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric( - vertical: 8, horizontal: 0), + vertical: 8, + horizontal: 0, + ), minimumSize: const Size(50, kMinInteractiveDimension), backgroundColor: diff --git a/lib/pages/basket.dart b/lib/pages/basket.dart index 841c369..cd2c85b 100644 --- a/lib/pages/basket.dart +++ b/lib/pages/basket.dart @@ -2,10 +2,15 @@ import 'package:flutter/material.dart'; import 'package:gymlink_module_web/components/app_bar.dart'; import 'package:gymlink_module_web/components/heading.dart'; -class BasketPage extends StatelessWidget { - const BasketPage({ - super.key, - }); +//TODO: Вывод корзины из shared_prefs +class BasketPage extends StatefulWidget { + const BasketPage({super.key}); + + @override + State createState() => _BasketPageState(); +} + +class _BasketPageState extends State { @override Widget build(BuildContext context) { return const Scaffold( diff --git a/lib/pages/detail.dart b/lib/pages/detail.dart index 1ad8ec5..9edc86e 100644 --- a/lib/pages/detail.dart +++ b/lib/pages/detail.dart @@ -1,28 +1,105 @@ import 'package:flutter/material.dart'; import 'package:gymlink_module_web/components/app_bar.dart'; import 'package:gymlink_module_web/components/heading.dart'; +import 'package:gymlink_module_web/tools/prefs.dart'; //TODO: Сделать получение инфы через объект -class DetailPage extends StatelessWidget { +class DetailPage extends StatefulWidget { final String name; final String description; final String price; final String id; final Image image; - const DetailPage( - {super.key, - required this.name, - required this.description, - required this.price, - required this.id, - required this.image}); + const DetailPage({ + super.key, + required this.name, + required this.description, + required this.price, + required this.id, + required this.image, + }); + + @override + State createState() => _DetailPageState(); +} + +class _DetailPageState extends State { + bool isInCart = false; + int quantity = 0; + + @override + void initState() { + super.initState(); + getCart().then((value) { + debugPrint(value.toString()); + setState(() { + isInCart = value.any((element) => element['id'] == widget.id); + if (isInCart) { + quantity = value + .firstWhere((element) => element['id'] == widget.id)['count']; + } + }); + }); + } + + Widget _buildButton() { + if (!isInCart) { + return ElevatedButton( + onPressed: () async { + await addItemToCart(widget.id); + setState(() { + isInCart = true; + quantity = 1; + }); + }, + style: ElevatedButton.styleFrom( + backgroundColor: Theme.of(context).primaryColor, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(50)), + ), + foregroundColor: Colors.white, + padding: const EdgeInsetsDirectional.fromSTEB(34, 10, 34, 10)), + child: const Text('Добавить в корзину'), + ); + } else { + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + IconButton( + icon: const Icon(Icons.remove), + onPressed: () async { + await removeItemFromCart(widget.id); + setState(() { + if (quantity > 1) { + quantity--; + } else { + isInCart = false; + quantity = 0; + } + }); + }, + ), + Text('$quantity'), + IconButton( + icon: const Icon(Icons.add), + onPressed: () async { + await addItemToCart(widget.id); + setState(() { + quantity++; + }); + }, + ), + ], + ); + } + } @override Widget build(BuildContext context) { return Scaffold( appBar: const GymLinkAppBar(), body: Column(mainAxisAlignment: MainAxisAlignment.start, children: [ - GymLinkHeader(title: '$name - $id'), + GymLinkHeader(title: '${widget.name} - ${widget.id}'), Expanded( child: SingleChildScrollView( child: Padding( @@ -33,7 +110,7 @@ class DetailPage extends StatelessWidget { child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ - image, + widget.image, Padding( padding: const EdgeInsetsDirectional.fromSTEB(0, 30, 60, 60), @@ -50,7 +127,7 @@ class DetailPage extends StatelessWidget { padding: const EdgeInsetsDirectional.fromSTEB( 20, 15, 10, 15), child: Text( - description, + widget.description, style: Theme.of(context).textTheme.bodyMedium, ), ), @@ -66,24 +143,10 @@ class DetailPage extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ Text( - 'Стоимость $price', + 'Стоимость ${widget.price}', style: Theme.of(context).textTheme.bodyLarge, ), - ElevatedButton( - onPressed: () => {}, - style: ElevatedButton.styleFrom( - backgroundColor: - Theme.of(context).primaryColor, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all( - Radius.circular(50), - ), - ), - foregroundColor: Colors.white, - padding: const EdgeInsetsDirectional.fromSTEB( - 34, 10, 34, 10)), - child: const Text('Добавить в корзину'), - ) + _buildButton() ], ), ), diff --git a/lib/tools/prefs.dart b/lib/tools/prefs.dart new file mode 100644 index 0000000..3db9b80 --- /dev/null +++ b/lib/tools/prefs.dart @@ -0,0 +1,42 @@ +import 'dart:convert'; + +import 'package:shared_preferences/shared_preferences.dart'; + +Future addItemToCart(String id) async { + final prefs = await SharedPreferences.getInstance(); + String cartString = prefs.getString('cart') ?? "[]"; + List> cart = + List>.from(jsonDecode(cartString) as List); + final index = cart.indexWhere((element) => element['id'] == id); + if (index == -1) { + cart.add({'id': id, 'count': 1}); + } else { + cart[index]['count'] = cart[index]['count']! + 1; + } + prefs.setString('cart', jsonEncode(cart)); +} + +Future>> getCart() async { + final prefs = await SharedPreferences.getInstance(); + String cartString = prefs.getString('cart') ?? "[]"; + List> cart = + List>.from(jsonDecode(cartString) as List); + return cart; +} + +Future removeItemFromCart(String id) async { + final prefs = await SharedPreferences.getInstance(); + String cartString = prefs.getString('cart') ?? "[]"; + List> cart = + List>.from(jsonDecode(cartString) as List); + cart.removeWhere((element) => element['id'] == id && element['count'] == 1); + for (final item in cart) { + if (item['id'] == id) { + item['count'] = item['count']! - 1; + if (item['count'] == 0) { + cart.remove(item); + } + } + } + prefs.setString('cart', jsonEncode(cart)); +} diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 8236f57..997e35d 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,8 +5,10 @@ import FlutterMacOS import Foundation +import shared_preferences_foundation import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) } diff --git a/pubspec.lock b/pubspec.lock index 82a057e..99e0803 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -57,6 +57,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + file: + dependency: transitive + description: + name: file + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" + url: "https://pub.dev" + source: hosted + version: "7.0.0" flutter: dependency: "direct main" description: flutter @@ -75,6 +91,11 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" leak_tracker: dependency: transitive description: @@ -139,6 +160,102 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.0" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" + url: "https://pub.dev" + source: hosted + version: "2.2.1" + platform: + dependency: transitive + description: + name: platform + sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" + url: "https://pub.dev" + source: hosted + version: "3.1.4" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180 + url: "https://pub.dev" + source: hosted + version: "2.2.3" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "1ee8bf911094a1b592de7ab29add6f826a7331fb854273d55918693d5364a1f2" + url: "https://pub.dev" + source: hosted + version: "2.2.2" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c" + url: "https://pub.dev" + source: hosted + version: "2.3.5" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: "9aee1089b36bd2aafe06582b7d7817fd317ef05fc30e6ba14bff247d0933042a" + url: "https://pub.dev" + source: hosted + version: "2.3.0" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59" + url: "https://pub.dev" + source: hosted + version: "2.3.2" sky_engine: dependency: transitive description: flutter @@ -192,6 +309,70 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.1" + url_launcher: + dependency: "direct main" + description: + name: url_launcher + sha256: "6ce1e04375be4eed30548f10a315826fd933c1e493206eab82eed01f438c8d2e" + url: "https://pub.dev" + source: hosted + version: "6.2.6" + url_launcher_android: + dependency: transitive + description: + name: url_launcher_android + sha256: "360a6ed2027f18b73c8d98e159dda67a61b7f2e0f6ec26e86c3ada33b0621775" + url: "https://pub.dev" + source: hosted + version: "6.3.1" + url_launcher_ios: + dependency: transitive + description: + name: url_launcher_ios + sha256: "9149d493b075ed740901f3ee844a38a00b33116c7c5c10d7fb27df8987fb51d5" + url: "https://pub.dev" + source: hosted + version: "6.2.5" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811 + url: "https://pub.dev" + source: hosted + version: "3.1.1" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234 + url: "https://pub.dev" + source: hosted + version: "3.1.0" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + sha256: "8d9e750d8c9338601e709cd0885f95825086bd8b642547f26bda435aade95d8a" + url: "https://pub.dev" + source: hosted + version: "2.3.1" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + sha256: ecf9725510600aa2bb6d7ddabe16357691b6d2805f66216a97d1b881e21beff7 + url: "https://pub.dev" + source: hosted + version: "3.1.1" vector_math: dependency: transitive description: @@ -208,5 +389,30 @@ packages: url: "https://pub.dev" source: hosted version: "13.0.0" + web: + dependency: transitive + description: + name: web + sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" + url: "https://pub.dev" + source: hosted + version: "0.5.1" + win32: + dependency: transitive + description: + name: win32 + sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb" + url: "https://pub.dev" + source: hosted + version: "5.5.0" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d + url: "https://pub.dev" + source: hosted + version: "1.0.4" sdks: dart: ">=3.3.3 <4.0.0" + flutter: ">=3.19.0" diff --git a/pubspec.yaml b/pubspec.yaml index c9ae6b9..3baf9d5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -36,6 +36,7 @@ dependencies: # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.6 url_launcher: ^6.2.6 + shared_preferences: ^2.2.3 dev_dependencies: flutter_test: