From 1c6e236c108341ed7980dc976fd3452d5559f6cb Mon Sep 17 00:00:00 2001 From: Mayank Baranwal Date: Mon, 9 Sep 2024 02:54:54 +0530 Subject: [PATCH] App updated --- assets/{icecream.json => icecreams.json} | 147 +++++---- assets/splash_screen.jpeg | Bin 0 -> 13591 bytes lib/app/constants.dart | 5 + lib/app/view/app.dart | 39 ++- lib/bootstrap.dart | 19 +- lib/product_store/data/product_data.dart | 13 + lib/product_store/model/app_state_model.dart | 93 ++++++ lib/product_store/model/icecream.dart | 107 +++++++ lib/product_store/model/product.dart | 1 + lib/product_store/views/cart_view.dart | 278 ++++++++++++++++++ lib/product_store/views/home_view.dart | 46 +++ lib/product_store/views/icecream_view.dart | 36 +++ lib/product_store/views/login_view.dart | 93 ++++++ lib/product_store/views/search_view.dart | 75 +++++ lib/product_store/widgets/cart_item.dart | 38 +++ lib/product_store/widgets/product_item.dart | 43 +++ lib/product_store/widgets/search_bar.dart | 57 ++++ lib/themes/styles.dart | 134 +++++++++ macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.lock | 95 +++++- pubspec.yaml | 8 +- 21 files changed, 1235 insertions(+), 94 deletions(-) rename assets/{icecream.json => icecreams.json} (63%) create mode 100644 assets/splash_screen.jpeg create mode 100644 lib/app/constants.dart create mode 100644 lib/product_store/data/product_data.dart create mode 100644 lib/product_store/model/app_state_model.dart create mode 100644 lib/product_store/model/icecream.dart create mode 100644 lib/product_store/model/product.dart create mode 100644 lib/product_store/views/cart_view.dart create mode 100644 lib/product_store/views/home_view.dart create mode 100644 lib/product_store/views/icecream_view.dart create mode 100644 lib/product_store/views/login_view.dart create mode 100644 lib/product_store/views/search_view.dart create mode 100644 lib/product_store/widgets/cart_item.dart create mode 100644 lib/product_store/widgets/product_item.dart create mode 100644 lib/product_store/widgets/search_bar.dart create mode 100644 lib/themes/styles.dart diff --git a/assets/icecream.json b/assets/icecreams.json similarity index 63% rename from assets/icecream.json rename to assets/icecreams.json index 8af5b9d..bfdf65e 100644 --- a/assets/icecream.json +++ b/assets/icecreams.json @@ -1,18 +1,33 @@ { "icecreams": [ { - "id": 1, + "id": 0, "category": "Classic", "isFeatured": true, - "flavour": "Vanilla", - "description": "A rich and creamy classic vanilla ice cream.", - "toppings": ["Sprinkles", "Cherries", "Chocolate Syrup"], - "price": 59, - "image": "https://example.com/vanilla.png", + "flavour": "Cookies and Cream", + "description": "A creamy vanilla ice cream filled with chunks of chocolate cookies.", + "toppings": ["Cookie Crumbs", "Whipped Cream"], + "price": 146.75, + "image": "https://images.unsplash.com/photo-1642646682918-442f76c1ec74?w=500&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8N3x8Q29va2llcyUyMGFuZCUyMENyZWFtJTIwaWNlJTIwY3JlYW18ZW58MHx8MHx8fDA%3D", "ingredients": [ { "name": "Milk", "quantity": "200 ml" }, - { "name": "Sugar", "quantity": "50 g" }, - { "name": "Vanilla Extract", "quantity": "5 ml" } + { "name": "Cookies", "quantity": "50 g" }, + { "name": "Sugar", "quantity": "50 g" } + ] + }, + { + "id": 1, + "category": "Fruit", + "isFeatured": true, + "flavour": "Raspberry", + "description": "A tart and sweet raspberry ice cream.", + "toppings": ["Fresh Raspberries", "Chopped Nuts"], + "price": 180.15, + "image": "https://images.unsplash.com/photo-1505394033641-40c6ad1178d7?w=500&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8M3x8UmFzcGJlcnJ5JTIwaWNlY3JlYW18ZW58MHx8MHx8fDA%3D", + "ingredients": [ + { "name": "Milk", "quantity": "200 ml" }, + { "name": "Raspberries", "quantity": "100 g" }, + { "name": "Sugar", "quantity": "50 g" } ] }, { @@ -22,8 +37,8 @@ "flavour": "Chocolate", "description": "Rich and indulgent chocolate ice cream made from real cocoa.", "toppings": ["Whipped Cream", "Chocolate Chips"], - "price": 65, - "image": "https://example.com/chocolate.png", + "price": 185.57, + "image": "https://plus.unsplash.com/premium_photo-1674819647262-7b111521e2d5?w=500&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8OXx8Q2hvY29sYXRlJTIwaWNlJTIwY3JlYW18ZW58MHx8MHx8fDA%3D", "ingredients": [ { "name": "Milk", "quantity": "200 ml" }, { "name": "Cocoa Powder", "quantity": "30 g" }, @@ -37,8 +52,8 @@ "flavour": "Strawberry", "description": "A refreshing and fruity strawberry ice cream made with fresh strawberries.", "toppings": ["Fresh Strawberries", "Whipped Cream"], - "price": 71, - "image": "https://example.com/strawberry.png", + "price": 171.22, + "image": "https://images.unsplash.com/photo-1532678465554-94846274c297?w=500&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8M3x8U3RyYXdiZXJyeSUyMGljZSUyMGNyZWFtfGVufDB8fDB8fHww", "ingredients": [ { "name": "Milk", "quantity": "200 ml" }, { "name": "Strawberries", "quantity": "100 g" }, @@ -52,8 +67,8 @@ "flavour": "Mint Chocolate Chip", "description": "Cool and refreshing mint ice cream with chocolate chips.", "toppings": ["Mint Leaves", "Chocolate Chips"], - "price": 69, - "image": "https://example.com/mintchoc.png", + "price": 269.99, + "image": "https://images.unsplash.com/photo-1534706936160-d5ee67737249?w=500&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8M3x8TWludCUyMGljZSUyMGNyZWFtfGVufDB8fDB8fHww", "ingredients": [ { "name": "Milk", "quantity": "200 ml" }, { "name": "Mint Extract", "quantity": "5 ml" }, @@ -67,8 +82,8 @@ "flavour": "Mango", "description": "Delicious mango ice cream with a tropical twist.", "toppings": ["Mango Slices", "Coconut Shavings"], - "price": 77, - "image": "https://example.com/mango.png", + "price": 177.62, + "image": "https://images.unsplash.com/photo-1591677445540-08028eeb3021?w=500&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8Mnx8TWFuZ28lMjBpY2UlMjBjcmVhbXxlbnwwfHwwfHx8MA%3D%3D", "ingredients": [ { "name": "Milk", "quantity": "200 ml" }, { "name": "Mango Puree", "quantity": "100 g" }, @@ -78,12 +93,12 @@ { "id": 6, "category": "Chocolate", - "isFeatured": true, + "isFeatured": false, "flavour": "Dark Chocolate", "description": "Decadent dark chocolate ice cream for true chocolate lovers.", "toppings": ["Cacao Nibs", "Chocolate Sauce"], - "price": 83, - "image": "https://example.com/darkchocolate.png", + "price": 243.25, + "image": "https://images.unsplash.com/photo-1569429378981-f4c5ba689a9a?w=500&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8Mzl8fENob2NvbGF0ZSUyMGljZSUyMGNyZWFtfGVufDB8fDB8fHww", "ingredients": [ { "name": "Milk", "quantity": "200 ml" }, { "name": "Dark Chocolate", "quantity": "50 g" }, @@ -97,8 +112,8 @@ "flavour": "Blueberry", "description": "Creamy blueberry ice cream packed with fresh blueberries.", "toppings": ["Blueberry Sauce", "Whipped Cream"], - "price": 76, - "image": "https://example.com/blueberry.png", + "price": 326.18, + "image": "https://plus.unsplash.com/premium_photo-1713895023518-37596f5e8c5e?w=500&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MXx8Qmx1ZWJlcnJ5JTIwaWNlJTIwY3JlYW18ZW58MHx8MHx8fDA%3D", "ingredients": [ { "name": "Milk", "quantity": "200 ml" }, { "name": "Blueberries", "quantity": "100 g" }, @@ -112,8 +127,8 @@ "flavour": "Pistachio", "description": "Rich and creamy pistachio ice cream made from real pistachios.", "toppings": ["Chopped Pistachios", "Whipped Cream"], - "price": 86, - "image": "https://example.com/pistachio.png", + "price": 346.25, + "image": "https://images.unsplash.com/photo-1543255006-d6395b6f1171?w=500&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8NHx8UGlzdGFjaGlvJTIwaWNlJTIwY3JlYW18ZW58MHx8MHx8fDA%3D", "ingredients": [ { "name": "Milk", "quantity": "200 ml" }, { "name": "Pistachios", "quantity": "50 g" }, @@ -122,17 +137,17 @@ }, { "id": 9, - "category": "Fruit", + "category": "Classic", "isFeatured": true, - "flavour": "Raspberry", - "description": "A tart and sweet raspberry ice cream.", - "toppings": ["Fresh Raspberries", "Chopped Nuts"], - "price": 70, - "image": "https://example.com/raspberry.png", + "flavour": "Vanilla", + "description": "A rich and creamy classic vanilla ice cream.", + "toppings": ["Sprinkles", "Cherries", "Chocolate Syrup"], + "price": 139.99, + "image": "https://images.unsplash.com/photo-1570197788417-0e82375c9371?w=500&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8NHx8VmFuaWxsYSUyMGljZWNyZWFtfGVufDB8fDB8fHww", "ingredients": [ { "name": "Milk", "quantity": "200 ml" }, - { "name": "Raspberries", "quantity": "100 g" }, - { "name": "Sugar", "quantity": "50 g" } + { "name": "Sugar", "quantity": "50 g" }, + { "name": "Vanilla Extract", "quantity": "5 ml" } ] }, { @@ -142,8 +157,8 @@ "flavour": "Coconut", "description": "Tropical coconut ice cream with a rich and smooth texture.", "toppings": ["Shredded Coconut", "Chocolate Chips"], - "price": 78, - "image": "https://example.com/coconut.png", + "price": 148.25, + "image": "https://images.unsplash.com/photo-1534095256953-6249f0cd07f7?w=500&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8Mnx8Q29jb251dCUyMGljZWNyZWFtfGVufDB8fDB8fHww", "ingredients": [ { "name": "Coconut Milk", "quantity": "200 ml" }, { "name": "Coconut Shavings", "quantity": "30 g" }, @@ -157,8 +172,8 @@ "flavour": "Butter Pecan", "description": "Buttery, rich ice cream with toasted pecans.", "toppings": ["Pecan Pieces", "Caramel Drizzle"], - "price": 83, - "image": "https://example.com/butterpecan.png", + "price": 384.99, + "image": "https://plus.unsplash.com/premium_photo-1675279010961-8a6679ff03da?w=500&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MXx8aWNlJTIwY3JlYW0lMjBjb25lfGVufDB8fDB8fHww", "ingredients": [ { "name": "Milk", "quantity": "200 ml" }, { "name": "Pecans", "quantity": "50 g" }, @@ -167,28 +182,13 @@ }, { "id": 12, - "category": "Chocolate", - "isFeatured": true, - "flavour": "Rocky Road", - "description": "Chocolate ice cream loaded with marshmallows and almonds.", - "toppings": ["Marshmallows", "Almonds", "Chocolate Syrup"], - "price": 89, - "image": "https://example.com/rockyroad.png", - "ingredients": [ - { "name": "Milk", "quantity": "200 ml" }, - { "name": "Marshmallows", "quantity": "30 g" }, - { "name": "Almonds", "quantity": "20 g" } - ] - }, - { - "id": 13, "category": "Exotic", "isFeatured": false, "flavour": "Passion Fruit", "description": "A tangy passion fruit ice cream with a tropical flavor.", "toppings": ["Passion Fruit Syrup", "Shaved Coconut"], - "price": 82, - "image": "https://example.com/passionfruit.png", + "price": 262.25, + "image": "https://images.unsplash.com/photo-1468769398733-d97298de3a06?w=500&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1yZWxhdGVkfDJ8fHxlbnwwfHx8fHw%3D", "ingredients": [ { "name": "Milk", "quantity": "200 ml" }, { "name": "Passion Fruit Puree", "quantity": "50 g" }, @@ -196,29 +196,14 @@ ] }, { - "id": 14, - "category": "Classic", - "isFeatured": true, - "flavour": "Cookies and Cream", - "description": "A creamy vanilla ice cream filled with chunks of chocolate cookies.", - "toppings": ["Cookie Crumbs", "Whipped Cream"], - "price": 71, - "image": "https://example.com/cookiescream.png", - "ingredients": [ - { "name": "Milk", "quantity": "200 ml" }, - { "name": "Cookies", "quantity": "50 g" }, - { "name": "Sugar", "quantity": "50 g" } - ] - }, - { - "id": 15, + "id": 13, "category": "Classic", "isFeatured": true, "flavour": "Butterscotch", "description": "Smooth and creamy butterscotch ice cream with a buttery flavor.", "toppings": ["Caramel Sauce", "Crushed Nuts"], - "price": 83, - "image": "https://example.com/butterscotch.png", + "price": 153.99, + "image": "https://plus.unsplash.com/premium_photo-1675279010969-e85bfbd402dc?w=500&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1yZWxhdGVkfDh8fHxlbnwwfHx8fHw%3D", "ingredients": [ { "name": "Milk", "quantity": "200 ml" }, { "name": "Butterscotch Chips", "quantity": "50 g" }, @@ -226,20 +211,20 @@ ] }, { - "id": 16, - "category": "Chocolate", - "isFeatured": false, - "flavour": "Belgian Chocolate", - "description": "Rich and creamy Belgian chocolate ice cream with a luxurious flavor.", - "toppings": ["Dark Chocolate Shavings", "Chocolate Sauce"], - "price": 83, - "image": "https://example.com/belgianchocolate.png", + "id": 14, + "category": "Caramel", + "isFeatured": true, + "flavour": "Caramel Swirl", + "description": "Creamy vanilla ice cream with rich caramel swirls.", + "toppings": ["Caramel Syrup", "Sea Salt", "Whipped Cream"], + "price": 179.99, + "image": "https://plus.unsplash.com/premium_photo-1707494324528-38cc510756e5?w=500&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1yZWxhdGVkfDR8fHxlbnwwfHx8fHw%3D", "ingredients": [ { "name": "Milk", "quantity": "200 ml" }, - { "name": "Belgian Chocolate", "quantity": "50 g" }, - { "name": "Sugar", "quantity": "40 g" } + { "name": "Caramel Syrup", "quantity": "50 ml" }, + { "name": "Sea Salt", "quantity": "5 g" } ] - } + } ] } \ No newline at end of file diff --git a/assets/splash_screen.jpeg b/assets/splash_screen.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..849e4777d52c7a0536b100709963dcdd92e8a32c GIT binary patch literal 13591 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 VxAdaptive( + scaleType: VxAdaptiveScaleType.width, + designWidth: Vx.isMobileOS ? context.screenWidth : 1000, + builder: (context, scaled) => MaterialApp( + debugShowCheckedModeBanner: false, + theme: ThemeData( + useMaterial3: true, + brightness: vxData.isDarkMode ? Brightness.dark : Brightness.light, + colorScheme: vxData.isDarkMode + ? Styles.darkColorScheme + : Styles.lightColorScheme, + ), + // darkTheme: ThemeData( + // useMaterial3: true, + // colorScheme: Styles.darkColorScheme, + // ), + localizationsDelegates: AppLocalizations.localizationsDelegates, + supportedLocales: AppLocalizations.supportedLocales, + home: Constants.prefs!.getBool('isLoggedIn') == true + ? const HomeView() + : const LoginView(), ), - useMaterial3: true, ), - localizationsDelegates: AppLocalizations.localizationsDelegates, - supportedLocales: AppLocalizations.supportedLocales, - home: const CounterPage(), ); } } + +class MyStore extends VxStore {} diff --git a/lib/bootstrap.dart b/lib/bootstrap.dart index 558f731..46c1034 100644 --- a/lib/bootstrap.dart +++ b/lib/bootstrap.dart @@ -2,7 +2,12 @@ import 'dart:async'; import 'dart:developer'; import 'package:bloc/bloc.dart'; +import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; +import 'package:ice_cream_cart/app/constants.dart'; +import 'package:ice_cream_cart/product_store/model/app_state_model.dart'; +import 'package:provider/provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; class AppBlocObserver extends BlocObserver { const AppBlocObserver(); @@ -26,8 +31,20 @@ Future bootstrap(FutureOr Function() builder) async { }; Bloc.observer = const AppBlocObserver(); + WidgetsFlutterBinding.ensureInitialized(); + await SystemChrome.setPreferredOrientations([ + DeviceOrientation.portraitUp, + DeviceOrientation.portraitDown, + ]); + + Constants.prefs = await SharedPreferences.getInstance(); + // Add cross-flavor configuration here - runApp(await builder()); + runApp(ChangeNotifierProvider( + create: (context) => AppStateModel()..loadProducts(), + child: await builder(), + ), + ); } diff --git a/lib/product_store/data/product_data.dart b/lib/product_store/data/product_data.dart new file mode 100644 index 0000000..34e2144 --- /dev/null +++ b/lib/product_store/data/product_data.dart @@ -0,0 +1,13 @@ +import 'dart:convert'; + +import 'package:flutter/services.dart'; +import 'package:ice_cream_cart/product_store/model/icecream.dart'; + +class ProductData { + static Future> loadAllIcecreams() async { + final res = await rootBundle.loadString('assets/icecreams.json'); + final iceCreamData = + IceCreamData.fromJson(jsonDecode(res) as Map); + return iceCreamData.icecreams!; + } +} diff --git a/lib/product_store/model/app_state_model.dart b/lib/product_store/model/app_state_model.dart new file mode 100644 index 0000000..ea2abae --- /dev/null +++ b/lib/product_store/model/app_state_model.dart @@ -0,0 +1,93 @@ +import 'package:flutter/foundation.dart'; +import 'package:ice_cream_cart/product_store/data/product_data.dart'; +import 'package:ice_cream_cart/product_store/model/icecream.dart'; + +double _salesTaxRate = 0.18; +double _shippingCostPerItem = 10; + +class AppStateModel extends ChangeNotifier { + int currentIndex = 0; + + void changeIndex(int index) { + currentIndex = index; + notifyListeners(); + } + + List _availableProducts = []; + + final _productsInCart = {}; + + Map get productsInCart { + return Map.from(_productsInCart); + } + + Icecreams getProductById(int id) { + return _availableProducts.firstWhere((element) => element.id == id); + } + + List getProducts() { + return _availableProducts; + } + + int get totalCartQuantity { + return _productsInCart.values.fold(0, (sum, val) => sum + val); + } + + double get subtotalCost { + return _productsInCart.keys + .map((id) => _availableProducts[id].price! * _productsInCart[id]!) + .fold(0, (sum, val) => sum + val); + } + + double get shippingCost { + return _shippingCostPerItem * + _productsInCart.values.fold(0.0, (sum, val) => sum + val); + } + + double get tax { + return subtotalCost * _salesTaxRate; + } + + double get totalCost { + return subtotalCost + shippingCost + tax; + } + + void addProductToCart(int productId) { + if (!_productsInCart.containsKey(productId)) { + _productsInCart[productId] = 1; + } else { + _productsInCart[productId] = _productsInCart[productId]! + 1; + } + notifyListeners(); + } + + void removeProductFromCart(int productId) { + if (!_productsInCart.containsKey(productId)) { + if (_productsInCart[productId] == 1) { + _productsInCart.remove(productId); + } else { + _productsInCart[productId] = _productsInCart[productId]! - 1; + } + } + notifyListeners(); + } + + void clearCart() { + _productsInCart.clear(); + notifyListeners(); + } + + List search(String query) { + return _availableProducts + .where( + (product) => + product.flavour!.toLowerCase().contains(query.toLowerCase()), + ) + .toList(); + } + + Future loadProducts() async { + _availableProducts = await ProductData.loadAllIcecreams(); + notifyListeners(); + } +} diff --git a/lib/product_store/model/icecream.dart b/lib/product_store/model/icecream.dart new file mode 100644 index 0000000..21e5305 --- /dev/null +++ b/lib/product_store/model/icecream.dart @@ -0,0 +1,107 @@ +import 'package:ice_cream_cart/product_store/model/product.dart'; + +class IceCreamData { + IceCreamData({this.icecreams}); + + IceCreamData.fromJson(Map json) { + if (json['icecreams'] != null) { + icecreams = []; + json['icecreams'].forEach((v) { + icecreams!.add(Icecreams.fromJson(v as Map)); + }); + } + } + + List? icecreams; + + Map toJson() { + final data = {}; + if (icecreams != null) { + data['icecreams'] = icecreams!.map((v) => v.toJson()).toList(); + } + return data; + } +} + +class Icecreams implements Product { + + Icecreams({ + this.id, + this.category, + this.isFeatured, + this.flavour, + this.description, + this.toppings, + this.price, + this.image, + this.ingredients, + }); + + Icecreams.fromJson(Map json) { + id = json['id'] as int?; + category = json['category'] as String?; + isFeatured = json['isFeatured'] as bool?; + flavour = json['flavour'] as String?; + description = json['description'] as String?; + toppings = json['toppings'].cast() as List?; + price = json['price'] as double?; + image = json['image'] as String?; + if (json['ingredients'] != null) { + ingredients = []; + json['ingredients'].forEach((v) { + ingredients!.add(Ingredients.fromJson(v as Map)); + }); + } + } + + int? id; + String? category; + bool? isFeatured; + String? flavour; + String? description; + List? toppings; + double? price; + String? image; + List? ingredients; + + Map toJson() { + final data = {}; + data['id'] = id; + data['category'] = category; + data['isFeatured'] = isFeatured; + data['flavour'] = flavour; + data['description'] = description; + data['toppings'] = toppings; + data['price'] = price; + data['image'] = image; + if (ingredients != null) { + data['ingredients'] = ingredients!.map((v) => v.toJson()).toList(); + } + return data; + } + + @override + String toString() { + return 'Name: $flavour, Price: $price'; + } +} + +class Ingredients { + + Ingredients({this.name, this.quantity}); + + Ingredients.fromJson(Map json) { + name = json['name'] as String?; + quantity = json['quantity'] as String?; + } + + String? name; + String? quantity; + + Map toJson() { + final data = {}; + data['name'] = name; + data['quantity'] = quantity; + return data; + } +} diff --git a/lib/product_store/model/product.dart b/lib/product_store/model/product.dart new file mode 100644 index 0000000..fa143e8 --- /dev/null +++ b/lib/product_store/model/product.dart @@ -0,0 +1 @@ +class Product {} diff --git a/lib/product_store/views/cart_view.dart b/lib/product_store/views/cart_view.dart new file mode 100644 index 0000000..990df28 --- /dev/null +++ b/lib/product_store/views/cart_view.dart @@ -0,0 +1,278 @@ +import 'package:flutter/material.dart'; +import 'package:ice_cream_cart/product_store/model/app_state_model.dart'; +import 'package:ice_cream_cart/product_store/widgets/cart_item.dart'; +import 'package:ice_cream_cart/themes/styles.dart'; +import 'package:intl/intl.dart'; +import 'package:ionicons/ionicons.dart'; +import 'package:provider/provider.dart'; + +class CartView extends StatefulWidget { + const CartView({super.key}); + + @override + State createState() => _CartViewState(); +} + +class _CartViewState extends State { + String? name; + String? email; + String? mobile; + String? address; + DateTime? dateTime = DateTime.now(); + final formKey = GlobalKey(); + + Widget _buildName() { + return TextFormField( + decoration: const InputDecoration( + border: OutlineInputBorder(), + labelText: 'Name', + prefixIcon: Icon( + Ionicons.person_outline, + ), + ), + keyboardType: TextInputType.name, + validator: (String? value) { + if (value!.isEmpty) { + return 'Name is Required'; + } + return null; + }, + onSaved: (String? value) { + name = value; + }, + onChanged: (value) => setState(() => name = value), + ); + } + + Widget _buildEmail() { + return TextFormField( + decoration: const InputDecoration( + border: OutlineInputBorder(), + labelText: 'Email', + prefixIcon: Icon( + Ionicons.mail_outline, + ), + ), + keyboardType: TextInputType.emailAddress, + validator: (String? value) { + if (value!.isEmpty) { + return 'Email is Required'; + } + if (!RegExp(r'\S+@\S+\.\S+').hasMatch(value)) { + return 'Please enter a valid email address'; + } + return null; + }, + onSaved: (String? value) { + email = value; + }, + onChanged: (value) => setState(() => email = value), + ); + } + + Widget _buildMobile() { + return TextFormField( + decoration: const InputDecoration( + border: OutlineInputBorder(), + labelText: 'Mobile', + prefixIcon: Icon( + Ionicons.call_outline, + ), + ), + keyboardType: TextInputType.phone, + validator: (String? value) { + if (value!.isEmpty) { + return 'Mobile is Required'; + } + return null; + }, + onSaved: (String? value) { + mobile = value; + }, + onChanged: (value) => setState(() => mobile = value), + ); + } + + Widget _buildAddress() { + return TextFormField( + decoration: const InputDecoration( + border: OutlineInputBorder(), + labelText: 'Address', + prefixIcon: Icon( + Ionicons.location_outline, + ), + ), + keyboardType: TextInputType.streetAddress, + validator: (String? value) { + if (value!.isEmpty) { + return 'Address is Required'; + } + return null; + }, + onSaved: (String? value) { + address = value; + }, + onChanged: (value) => setState(() => address = value), + ); + } + + Widget _buildDatePicker() { + return InkWell( + onTap: () async { + final newTime = await showDatePicker( + context: context, + initialDate: dateTime!, + firstDate: DateTime(2000), + lastDate: DateTime(2026), + ); + if (newTime != null) { + setState(() { + dateTime = newTime; + }); + } + }, + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Row( + children: [ + Icon( + Ionicons.time_outline, + size: 28, + ), + SizedBox(width: 6), + Text( + 'Delivery date', + style: Styles.deliveryTimeLabel, + ), + ], + ), + Text( + DateFormat.yMMMd().add_jm().format(dateTime!), + style: Styles.deliveryTime, + ), + ], + ), + ], + ), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Cart'), + ), + body: Consumer( + builder: (context, value, child) { + return ListView( + children: [ + ExpansionTile( + title: const Text('Address Details'), + children: [ + Form( + key: formKey, + child: Column( + children: [ + Padding( + padding: const EdgeInsets.all(16), + child: _buildName(), + ), + Padding( + padding: const EdgeInsets.all(16), + child: _buildEmail(), + ), + Padding( + padding: const EdgeInsets.all(16), + child: _buildMobile(), + ), + Padding( + padding: const EdgeInsets.all(16), + child: _buildAddress(), + ), + Padding( + padding: const EdgeInsets.all(16), + child: _buildDatePicker(), + ), + ], + ), + ), + ], + ), + const Divider(), + if (value.productsInCart.isNotEmpty) ...[ + ListView.builder( + itemBuilder: (context, index) { + return CartItem( + product: value.getProductById( + value.productsInCart.keys.toList()[index], + ), + quantity: value.productsInCart.values.toList()[index], + ); + }, + itemCount: value.productsInCart.length, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + ), + const Divider(), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + 'Shipping + Tax', + style: Styles.productRowItemPrice, + ), + Text( + '₹ ${value.shippingCost.toStringAsFixed(2)} + ${value.tax.toStringAsFixed(2)}', + style: Styles.productRowItemPrice, + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.all(16), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + 'Total', + style: Styles.productRowItemName, + ), + Text( + '₹ ${value.totalCost.toStringAsFixed(2)}', + style: Styles.productRowItemName, + ), + ], + ), + ), + const Divider(), + Padding( + padding: const EdgeInsets.all(16), + child: ElevatedButton( + onPressed: () { + if (formKey.currentState!.validate()) { + formKey.currentState!.save(); + value.clearCart(); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text('Order Placed Successfully'), + ), + ); + } + }, + child: const Text('Place Order'), + ), + ), + ], + ], + ); + }, + ), + ); + } +} diff --git a/lib/product_store/views/home_view.dart b/lib/product_store/views/home_view.dart new file mode 100644 index 0000000..4207666 --- /dev/null +++ b/lib/product_store/views/home_view.dart @@ -0,0 +1,46 @@ +import 'package:flutter/material.dart'; +import 'package:ice_cream_cart/product_store/model/app_state_model.dart'; +import 'package:ice_cream_cart/product_store/views/cart_view.dart'; +import 'package:ice_cream_cart/product_store/views/icecream_view.dart'; +import 'package:ice_cream_cart/product_store/views/search_view.dart'; +import 'package:ionicons/ionicons.dart'; +import 'package:provider/provider.dart'; + +class HomeView extends StatelessWidget { + const HomeView({super.key}); + + @override + Widget build(BuildContext context) { + final model = Provider.of(context); + return Scaffold( + body: SafeArea( + child: IndexedStack( + index: model.currentIndex, + children: const [ + IceCreamView(), + SearchView(), + CartView(), + ], + ), + ), + bottomNavigationBar: NavigationBar( + selectedIndex: model.currentIndex, + onDestinationSelected: model.changeIndex, + destinations: const [ + NavigationDestination( + icon: Icon(Ionicons.ice_cream_outline), + label: 'Ice Creams', + ), + NavigationDestination( + icon: Icon(Ionicons.search_outline), + label: 'Search', + ), + NavigationDestination( + icon: Icon(Ionicons.cart_outline), + label: 'Cart', + ), + ], + ), + ); + } +} diff --git a/lib/product_store/views/icecream_view.dart b/lib/product_store/views/icecream_view.dart new file mode 100644 index 0000000..406ed61 --- /dev/null +++ b/lib/product_store/views/icecream_view.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; +import 'package:ice_cream_cart/product_store/model/app_state_model.dart'; +import 'package:ice_cream_cart/product_store/widgets/product_item.dart'; +import 'package:provider/provider.dart'; +import 'package:velocity_x/velocity_x.dart'; + +class IceCreamView extends StatelessWidget { + const IceCreamView({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Ice Creams'), + actions: [ + const VxDarkModeButton( + showSingleIcon: true, + ).p12(), + ], + ), + body: Consumer( + builder: (context, value, child) { + final products = value.getProducts(); + return ListView.builder( + itemBuilder: (context, index) { + return ProductItem( + icecream: products[index], + ); + }, + itemCount: products.length, + ); + }, + ), + ); + } +} diff --git a/lib/product_store/views/login_view.dart b/lib/product_store/views/login_view.dart new file mode 100644 index 0000000..cc2b7c1 --- /dev/null +++ b/lib/product_store/views/login_view.dart @@ -0,0 +1,93 @@ +import 'package:flutter/material.dart'; +import 'package:ice_cream_cart/app/constants.dart'; +import 'package:ice_cream_cart/product_store/views/home_view.dart'; + +class LoginView extends StatefulWidget { + const LoginView({super.key}); + + @override + State createState() => _LoginViewState(); +} + +class _LoginViewState extends State + with SingleTickerProviderStateMixin { + late AnimationController _controller; + late Animation _animation; + + @override + void initState() { + super.initState(); + _controller = + AnimationController(vsync: this, duration: const Duration(seconds: 1)); + _animation = Tween(begin: 0, end: 100).animate(_controller) + ..addListener(() { + setState(() {}); + }); + _controller.forward(); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Material( + child: Stack( + fit: StackFit.expand, + children: [ + AnimatedContainer( + duration: const Duration(seconds: 1), + child: Image.asset( + 'assets/splash_screen.jpeg', + fit: BoxFit.cover, + height: double.infinity, + color: Colors.black.withOpacity(0.7), + colorBlendMode: BlendMode.darken, + ), + ), + const SizedBox(height: 36), + Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + FlutterLogo( + size: _animation.value, + ), + const SizedBox(height: 16), + AnimatedOpacity( + duration: const Duration(seconds: 1), + opacity: _animation.value / 100, + child: const Text( + 'Frozen Delights', + style: TextStyle( + color: Colors.white, + fontSize: 24, + fontWeight: FontWeight.bold, + ), + ), + ), + const SizedBox(height: 16), + ElevatedButton( + onPressed: () { + Constants.prefs!.setBool('isLoggedIn', true); + Navigator.pushAndRemoveUntil( + context, + MaterialPageRoute( + builder: (context) => const HomeView(), + ), + (route) => false, + ); + }, + child: const Text('Begin'), + ), + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/product_store/views/search_view.dart b/lib/product_store/views/search_view.dart new file mode 100644 index 0000000..7287575 --- /dev/null +++ b/lib/product_store/views/search_view.dart @@ -0,0 +1,75 @@ +import 'package:flutter/material.dart'; +import 'package:ice_cream_cart/product_store/model/app_state_model.dart'; +import 'package:ice_cream_cart/product_store/widgets/product_item.dart'; +import 'package:ice_cream_cart/product_store/widgets/search_bar.dart'; +import 'package:provider/provider.dart'; + +class SearchView extends StatefulWidget { + const SearchView({super.key}); + + @override + State createState() => _SearchViewState(); +} + +class _SearchViewState extends State { + late final TextEditingController _controller; + late final FocusNode _focusNode; + + String _query = ''; + + @override + void initState() { + super.initState(); + _controller = TextEditingController(text: _query) + ..addListener(_onQueryChanged); + _focusNode = FocusNode(); + } + + @override + void dispose() { + _focusNode.dispose(); + _controller.dispose(); + super.dispose(); + } + + void _onQueryChanged() { + setState(() { + _query = _controller.text; + }); + } + + Widget _buildSearchBox() { + return Padding( + padding: const EdgeInsets.all(8), + child: MySearchBar(controller: _controller, focusNode: _focusNode), + ); + } + + @override + Widget build(BuildContext context) { + final model = Provider.of(context); + final filteredProducts = model.search(_query); + return Scaffold( + appBar: AppBar( + title: const Text('Search'), + ), + body: SafeArea( + child: Column( + children: [ + _buildSearchBox(), + Expanded( + child: ListView.builder( + itemBuilder: (context, index) { + return ProductItem( + icecream: filteredProducts[index], + ); + }, + itemCount: filteredProducts.length, + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/product_store/widgets/cart_item.dart b/lib/product_store/widgets/cart_item.dart new file mode 100644 index 0000000..afc8ee6 --- /dev/null +++ b/lib/product_store/widgets/cart_item.dart @@ -0,0 +1,38 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:ice_cream_cart/product_store/model/icecream.dart'; +import 'package:ice_cream_cart/themes/styles.dart'; + +class CartItem extends StatelessWidget { + const CartItem({ + required this.product, + required this.quantity, + super.key, + }); + + final Icecreams product; + final int quantity; + + @override + Widget build(BuildContext context) { + return SafeArea( + child: ListTile( + leading: CircleAvatar( + backgroundImage: CachedNetworkImageProvider(product.image!), + ), + title: Text( + product.flavour!, + style: Styles.productRowItemName, + ), + subtitle: Text( + '${quantity > 1 ? '$quantity x ' : ''}₹ ${product.price}', + style: Styles.productRowItemName, + ), + trailing: Text( + '₹ ${(product.price! * quantity).toStringAsFixed(2)}', + style: Styles.productRowItemName, + ), + ), + ); + } +} diff --git a/lib/product_store/widgets/product_item.dart b/lib/product_store/widgets/product_item.dart new file mode 100644 index 0000000..d283c5c --- /dev/null +++ b/lib/product_store/widgets/product_item.dart @@ -0,0 +1,43 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:ice_cream_cart/product_store/model/app_state_model.dart'; +import 'package:ice_cream_cart/product_store/model/icecream.dart'; + +import 'package:ice_cream_cart/themes/styles.dart'; +import 'package:provider/provider.dart'; +import 'package:velocity_x/velocity_x.dart'; + +class ProductItem extends StatelessWidget { + const ProductItem({required this.icecream, super.key}); + + final Icecreams icecream; + + @override + Widget build(BuildContext context) { + return ListTile( + leading: CircleAvatar( + backgroundImage: CachedNetworkImageProvider(icecream.image!), + ), + title: Text( + icecream.flavour!, + style: Styles.productRowItemName, + ), + subtitle: Text( + '₹ ${icecream.price}', + style: Styles.productRowItemName, + ), + trailing: IconButton( + icon: const Icon(Icons.add_circle_outline), + onPressed: () { + Provider.of(context, listen: false) + .addProductToCart(icecream.id!); + VxToast.show( + context, + msg: 'Added to cart', + position: VxToastPosition.center, + ); + }, + ), + ); + } +} diff --git a/lib/product_store/widgets/search_bar.dart b/lib/product_store/widgets/search_bar.dart new file mode 100644 index 0000000..299f862 --- /dev/null +++ b/lib/product_store/widgets/search_bar.dart @@ -0,0 +1,57 @@ +import 'package:flutter/material.dart'; +import 'package:ice_cream_cart/themes/styles.dart'; + +class MySearchBar extends StatelessWidget { + const MySearchBar({ + required this.controller, + required this.focusNode, + super.key, + }); + + final TextEditingController controller; + final FocusNode focusNode; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(8), + child: Container( + decoration: BoxDecoration( + color: Theme.of(context).cardColor, + borderRadius: BorderRadius.circular(8), + boxShadow: [ + BoxShadow( + color: Colors.grey.withOpacity(0.5), + blurRadius: 4, + offset: const Offset(0, 2), + ), + ], + ), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Row( + children: [ + const Icon(Icons.search), + const SizedBox(width: 8), + Expanded( + child: TextField( + controller: controller, + focusNode: focusNode, + style: Styles.searchText, + decoration: const InputDecoration( + border: InputBorder.none, + hintText: 'Search', + ), + ), + ), + IconButton( + icon: const Icon(Icons.clear), + onPressed: controller.clear, + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/themes/styles.dart b/lib/themes/styles.dart new file mode 100644 index 0000000..e69a6f2 --- /dev/null +++ b/lib/themes/styles.dart @@ -0,0 +1,134 @@ +import 'package:flutter/material.dart'; + +abstract class Styles { + static const lightColorScheme = ColorScheme( + brightness: Brightness.light, + primary: Color(0xff3c6090), + surfaceTint: Color(0xff3c6090), + onPrimary: Color(0xffffffff), + primaryContainer: Color(0xffd4e3ff), + onPrimaryContainer: Color(0xff001c3a), + secondary: Color(0xff884b6b), + onSecondary: Color(0xffffffff), + secondaryContainer: Color(0xffffd8e8), + onSecondaryContainer: Color(0xff380726), + tertiary: Color(0xff39608f), + onTertiary: Color(0xffffffff), + tertiaryContainer: Color(0xffd3e4ff), + onTertiaryContainer: Color(0xff001c38), + error: Color(0xff8f4c37), + onError: Color(0xffffffff), + errorContainer: Color(0xffffdbd0), + onErrorContainer: Color(0xff3a0b00), + surface: Color(0xfffff8f8), + onSurface: Color(0xff21191c), + onSurfaceVariant: Color(0xff43474e), + outline: Color(0xff74777f), + outlineVariant: Color(0xffc3c6cf), + shadow: Color(0xff000000), + scrim: Color(0xff000000), + inverseSurface: Color(0xff372e31), + inversePrimary: Color(0xffa6c8ff), + primaryFixed: Color(0xffd4e3ff), + onPrimaryFixed: Color(0xff001c3a), + primaryFixedDim: Color(0xffa6c8ff), + onPrimaryFixedVariant: Color(0xff224876), + secondaryFixed: Color(0xffffd8e8), + onSecondaryFixed: Color(0xff380726), + secondaryFixedDim: Color(0xfffcb0d5), + onSecondaryFixedVariant: Color(0xff6c3353), + tertiaryFixed: Color(0xffd3e4ff), + onTertiaryFixed: Color(0xff001c38), + tertiaryFixedDim: Color(0xffa3c9fe), + onTertiaryFixedVariant: Color(0xff1e4876), + surfaceDim: Color(0xffe5d6da), + surfaceBright: Color(0xfffff8f8), + surfaceContainerLowest: Color(0xffffffff), + surfaceContainerLow: Color(0xfffff0f3), + surfaceContainer: Color(0xfffaeaee), + surfaceContainerHigh: Color(0xfff4e4e8), + surfaceContainerHighest: Color(0xffeedfe3), + ); + + static const darkColorScheme = ColorScheme( + brightness: Brightness.dark, + primary: Color(0xffa6c8ff), + surfaceTint: Color(0xffa6c8ff), + onPrimary: Color(0xff00315e), + primaryContainer: Color(0xff224876), + onPrimaryContainer: Color(0xffd4e3ff), + secondary: Color(0xfffcb0d5), + onSecondary: Color(0xff521d3c), + secondaryContainer: Color(0xff6c3353), + onSecondaryContainer: Color(0xffffd8e8), + tertiary: Color(0xffa3c9fe), + onTertiary: Color(0xff00315b), + tertiaryContainer: Color(0xff1e4876), + onTertiaryContainer: Color(0xffd3e4ff), + error: Color(0xffffb59f), + onError: Color(0xff561f0e), + errorContainer: Color(0xff723522), + onErrorContainer: Color(0xffffdbd0), + surface: Color(0xff191114), + onSurface: Color(0xffeedfe3), + onSurfaceVariant: Color(0xffc3c6cf), + outline: Color(0xff8d9199), + outlineVariant: Color(0xff43474e), + shadow: Color(0xff000000), + scrim: Color(0xff000000), + inverseSurface: Color(0xffeedfe3), + inversePrimary: Color(0xff3c6090), + primaryFixed: Color(0xffd4e3ff), + onPrimaryFixed: Color(0xff001c3a), + primaryFixedDim: Color(0xffa6c8ff), + onPrimaryFixedVariant: Color(0xff224876), + secondaryFixed: Color(0xffffd8e8), + onSecondaryFixed: Color(0xff380726), + secondaryFixedDim: Color(0xfffcb0d5), + onSecondaryFixedVariant: Color(0xff6c3353), + tertiaryFixed: Color(0xffd3e4ff), + onTertiaryFixed: Color(0xff001c38), + tertiaryFixedDim: Color(0xffa3c9fe), + onTertiaryFixedVariant: Color(0xff1e4876), + surfaceDim: Color(0xff191114), + surfaceBright: Color(0xff40373a), + surfaceContainerLowest: Color(0xff130c0f), + surfaceContainerLow: Color(0xff21191c), + surfaceContainer: Color(0xff251d20), + surfaceContainerHigh: Color(0xff30282b), + surfaceContainerHighest: Color(0xff3b3235), + ); + + static const TextStyle productRowItemName = TextStyle( + fontSize: 18, + fontStyle: FontStyle.normal, + fontWeight: FontWeight.normal, + ); + + static const TextStyle productRowTotal = TextStyle( + fontSize: 18, + fontStyle: FontStyle.normal, + fontWeight: FontWeight.bold, + ); + + static const TextStyle productRowItemPrice = TextStyle( + color: Color(0xFF8E8E93), + fontSize: 13, + fontWeight: FontWeight.w300, + ); + + static const TextStyle searchText = TextStyle( + fontSize: 14, + fontStyle: FontStyle.normal, + fontWeight: FontWeight.normal, + ); + + static const TextStyle deliveryTimeLabel = TextStyle( + color: Color(0xFFC2C2C2), + fontWeight: FontWeight.w300, + ); + + static const TextStyle deliveryTime = TextStyle( + color: Colors.grey, + ); +} diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 2bfe7e4..eefcc6d 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,9 +6,11 @@ import FlutterMacOS import Foundation import path_provider_foundation +import shared_preferences_foundation import sqflite func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) + SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) } diff --git a/pubspec.lock b/pubspec.lock index 384507c..59af4fa 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -38,6 +38,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.11.0" + auto_size_text_pk: + dependency: transitive + description: + name: auto_size_text_pk + sha256: ced55de5336fa7f438c1f5a9aa234e25d7a120c1d40d376a7cdc2af28cdb6995 + url: "https://pub.dev" + source: hosted + version: "3.0.0" bloc: dependency: "direct main" description: @@ -205,6 +213,11 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" frontend_server_client: dependency: transitive description: @@ -269,6 +282,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.4" + ionicons: + dependency: "direct main" + description: + name: ionicons + sha256: "5496bc65a16115ecf05b15b78f494ee4a8869504357668f0a11d689e970523cf" + url: "https://pub.dev" + source: hosted + version: "0.2.2" js: dependency: transitive description: @@ -470,7 +491,7 @@ packages: source: hosted version: "1.5.1" provider: - dependency: transitive + dependency: "direct main" description: name: provider sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c @@ -493,6 +514,62 @@ packages: url: "https://pub.dev" source: hosted version: "0.28.0" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: "746e5369a43170c25816cc472ee016d3a66bc13fcf430c0bc41ad7b4b2922051" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "480ba4345773f56acda9abf5f50bd966f581dac5d514e5fc4a18c62976bbba7e" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: c4b35f6cb8f63c147312c054ce7c2254c8066745125264f0c88739c417fc9d9f + url: "https://pub.dev" + source: hosted + version: "2.5.2" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: d2ca4132d3946fec2184261726b355836a82c33d7d5b67af32692aff18a4684e + url: "https://pub.dev" + source: hosted + version: "2.4.2" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" + url: "https://pub.dev" + source: hosted + version: "2.4.1" shelf: dependency: transitive description: @@ -666,6 +743,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + velocity_x: + dependency: "direct main" + description: + name: velocity_x + sha256: "99b910c80cc2010b184ef921f0af6894a8d632e13169cf77e6f6cb5a7f310698" + url: "https://pub.dev" + source: hosted + version: "4.2.1" very_good_analysis: dependency: "direct dev" description: @@ -682,6 +767,14 @@ packages: url: "https://pub.dev" source: hosted version: "14.2.5" + vxstate: + dependency: transitive + description: + name: vxstate + sha256: ed5a880018191c5cfed8528bd77f2a942b04847168ca12636a306c323d311086 + url: "https://pub.dev" + source: hosted + version: "2.3.0" watcher: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 938970c..0e60572 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,6 +16,10 @@ dependencies: sdk: flutter google_fonts: ^6.2.1 intl: ^0.19.0 + ionicons: ^0.2.2 + provider: ^6.1.2 + shared_preferences: ^2.3.2 + velocity_x: ^4.2.1 dev_dependencies: bloc_test: ^9.1.7 @@ -27,5 +31,5 @@ dev_dependencies: flutter: uses-material-design: true generate: true - -assets: assets/ + assets: + - assets/