From 135c9f94298eef4d0ef67a1bf78dbef1a59c10f6 Mon Sep 17 00:00:00 2001 From: Mikkel Troels Kongsted Date: Wed, 29 Jan 2025 13:48:33 +0100 Subject: [PATCH] cart page --- mobile/lib/main.dart | 31 ++--- mobile/lib/pages/all_products_page.dart | 22 +--- mobile/lib/pages/cart_page.dart | 150 +++++++++++++++++++++++- mobile/lib/pages/landing_page.dart | 43 +++---- mobile/lib/pages/log_in_page.dart | 4 +- mobile/lib/pages/product_page.dart | 4 +- mobile/lib/pages/register_page.dart | 4 +- mobile/lib/repos/cart.dart | 105 +++++++++++++++++ mobile/lib/widgets/primary_card.dart | 17 +++ 9 files changed, 315 insertions(+), 65 deletions(-) create mode 100644 mobile/lib/repos/cart.dart create mode 100644 mobile/lib/widgets/primary_card.dart diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index 33ca49f..c5219ab 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:mobile/repos/cart.dart'; import 'package:mobile/repos/product.dart'; import 'package:provider/provider.dart'; import 'pages/landing_page.dart'; @@ -15,36 +16,24 @@ class MyApp extends StatelessWidget { Widget build(BuildContext context) { return MultiProvider( providers: [ + ChangeNotifierProvider(create: (_) => BottomNavigationBarRepo()), ChangeNotifierProvider(create: (_) => ProductRepo()), - ChangeNotifierProvider(create: (_) => BottomNavigationBarRepo()) + ChangeNotifierProvider(create: (_) => CartRepo()), ], child: MaterialApp( title: 'Fresh Plaza', theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue), - scaffoldBackgroundColor: const Color(0xECF6F0FF), + scaffoldBackgroundColor: const Color(0xFFDFDFDF), + textTheme: const TextTheme( + headlineLarge: TextStyle(color: Color(0xFF000000), fontSize: 64), + bodyLarge: TextStyle(color: Color(0xFF000000), fontSize: 20), + bodyMedium: TextStyle(color: Color(0xFF000000), fontSize: 16), + ), useMaterial3: true, ), - home: const MyHomePage(title: 'Fresh Plaza'), + home: const LandingPage(), ), ); } } - -class MyHomePage extends StatefulWidget { - const MyHomePage({super.key, required this.title}); - - final String title; - - @override - State createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - @override - Widget build(BuildContext context) { - return const Scaffold( - body: LandingPage(), - ); - } -} diff --git a/mobile/lib/pages/all_products_page.dart b/mobile/lib/pages/all_products_page.dart index 07d57ff..fa91ca0 100644 --- a/mobile/lib/pages/all_products_page.dart +++ b/mobile/lib/pages/all_products_page.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:mobile/repos/product.dart'; +import 'package:mobile/widgets/primary_card.dart'; import 'package:provider/provider.dart'; import 'product_page.dart'; @@ -17,13 +18,7 @@ class ProductListItem extends StatelessWidget { @override Widget build(BuildContext context) { - return Container( - margin: const EdgeInsets.all(10), - height: 100, - decoration: BoxDecoration( - color: const Color(0xFFFFFFFF), - border: Border.all(color: const Color(0xFF666666)), - borderRadius: const BorderRadius.all(Radius.circular(10))), + return PrimaryCard( child: ElevatedButton( style: ButtonStyle( backgroundColor: WidgetStateProperty.all(Colors.transparent), @@ -45,16 +40,9 @@ class ProductListItem extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - name, - style: - const TextStyle(fontSize: 20, color: Colors.black), - ), - Text( - "${price.toString()} kr", - style: - const TextStyle(fontSize: 16, color: Colors.black), - ) + Text(name, style: Theme.of(context).textTheme.bodyLarge), + Text("${price.toString()} kr", + style: Theme.of(context).textTheme.bodyMedium), ], )), ClipRRect( diff --git a/mobile/lib/pages/cart_page.dart b/mobile/lib/pages/cart_page.dart index 1f86c8a..edc8cd1 100644 --- a/mobile/lib/pages/cart_page.dart +++ b/mobile/lib/pages/cart_page.dart @@ -1,10 +1,158 @@ import 'package:flutter/material.dart'; +import 'package:mobile/repos/cart.dart'; +import 'package:mobile/widgets/primary_button.dart'; +import 'package:mobile/widgets/primary_card.dart'; +import 'package:provider/provider.dart'; + +class CartItemView extends StatelessWidget { + final CartRepo cartRepo; + final int productId; + final String name; + final int price; + final String imagePath; + final int amount; + + const CartItemView( + {super.key, + required this.cartRepo, + required this.productId, + required this.name, + required this.price, + required this.imagePath, + required this.amount}); + + @override + Widget build(BuildContext context) { + return PrimaryCard( + child: Row( + children: [ + Expanded( + child: Container( + padding: const EdgeInsets.all(10), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Text(name, + style: Theme.of(context).textTheme.bodyLarge), + ), + Text("$price kr", + style: Theme.of(context).textTheme.bodyMedium), + ], + ), + ), + Row( + children: [ + Column( + children: [ + Expanded( + child: Text("$amount stk", + style: Theme.of(context).textTheme.bodyLarge), + ), + Row( + children: [ + IconButton( + onPressed: () { + cartRepo.incrementAmount(productId); + }, + icon: const Icon(Icons.add)), + IconButton( + onPressed: () { + cartRepo.decrementAmount(productId); + }, + icon: const Icon(Icons.remove)) + ], + ), + ], + ) + ], + ), + ], + )), + ), + IconButton( + onPressed: () { + cartRepo.removeCartItem(cartRepo.withProductId(productId)); + }, + icon: const Icon(Icons.delete_outline)), + Image(width: 100, image: AssetImage(imagePath)) + ], + ), + ); + } +} class CartPage extends StatelessWidget { const CartPage({super.key}); @override Widget build(BuildContext context) { - return Row(); + return Column( + children: [ + Expanded( + child: Consumer( + builder: (_, cartRepo, __) { + final cart = cartRepo.allCartItems(); + return ListView.builder( + shrinkWrap: true, + itemBuilder: (_, idx) => CartItemView( + cartRepo: cartRepo, + productId: cart[idx].product.id, + name: cart[idx].product.name, + price: cart[idx].product.price, + imagePath: "assets/${cart[idx].product.name}.png", + amount: cart[idx].amount), + itemCount: cart.length, + ); + }, + ), + ), + Container( + decoration: const BoxDecoration(color: Color(0xFFFFFFFF), boxShadow: [ + BoxShadow( + blurRadius: 10, + spreadRadius: -4, + ) + ]), + padding: const EdgeInsets.all(10), + child: Column( + children: [ + Row( + children: [ + Expanded( + child: Container( + margin: const EdgeInsets.only(right: 10), + child: PrimaryButton( + onPressed: () {}, child: const Text("Indtast vare")), + ), + ), + Expanded( + child: Container( + margin: const EdgeInsets.only(left: 10), + child: PrimaryButton( + onPressed: () {}, child: const Text("Skan vare")), + ), + ), + ], + ), + Row( + children: [ + Expanded( + child: Container( + margin: const EdgeInsets.only(top: 10), + child: PrimaryButton( + onPressed: () {}, child: const Text("Afslut indkøb")), + ), + ), + ], + ), + ], + ), + ), + ], + ); } } diff --git a/mobile/lib/pages/landing_page.dart b/mobile/lib/pages/landing_page.dart index c1b4946..1b25f05 100644 --- a/mobile/lib/pages/landing_page.dart +++ b/mobile/lib/pages/landing_page.dart @@ -8,25 +8,28 @@ class LandingPage extends StatelessWidget { @override Widget build(BuildContext context) { - return Row(mainAxisAlignment: MainAxisAlignment.center, children: [ - Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text("Fresh Plaza", style: TextStyle(fontSize: 64.0)), - PrimaryButton( - onPressed: () => { - Navigator.of(context).push(MaterialPageRoute( - builder: (context) => const LogInPage())) - }, - child: const Text("Log ind")), - PrimaryButton( - onPressed: () => { - Navigator.of(context).push(MaterialPageRoute( - builder: (context) => const RegisterPage())) - }, - child: const Text("Opret bruger")) - ], - ) - ]); + return Scaffold( + body: Row(mainAxisAlignment: MainAxisAlignment.center, children: [ + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text("Fresh Plaza", + style: Theme.of(context).textTheme.headlineLarge), + PrimaryButton( + onPressed: () => { + Navigator.of(context).push(MaterialPageRoute( + builder: (context) => const LogInPage())) + }, + child: const Text("Log ind")), + PrimaryButton( + onPressed: () => { + Navigator.of(context).push(MaterialPageRoute( + builder: (context) => const RegisterPage())) + }, + child: const Text("Opret bruger")) + ], + ) + ]), + ); } } diff --git a/mobile/lib/pages/log_in_page.dart b/mobile/lib/pages/log_in_page.dart index 9e2f70b..34f80f6 100644 --- a/mobile/lib/pages/log_in_page.dart +++ b/mobile/lib/pages/log_in_page.dart @@ -13,9 +13,9 @@ class LogInPage extends StatelessWidget { Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - const Text( + Text( "Log ind", - style: TextStyle(fontSize: 64), + style: Theme.of(context).textTheme.headlineLarge, ), const PrimaryInput( label: "Mail/Tlf", placeholderText: "f.eks. example@example.com"), diff --git a/mobile/lib/pages/product_page.dart b/mobile/lib/pages/product_page.dart index faf0691..d58258c 100644 --- a/mobile/lib/pages/product_page.dart +++ b/mobile/lib/pages/product_page.dart @@ -57,11 +57,11 @@ class ProductPage extends StatelessWidget { ), Text( product.name, - style: const TextStyle(fontSize: 20), + style: Theme.of(context).textTheme.bodyLarge, ), Text( "${product.price} kr", - style: const TextStyle(fontSize: 16), + style: Theme.of(context).textTheme.bodyLarge, ), Padding( padding: const EdgeInsets.only(top: 20, bottom: 20), diff --git a/mobile/lib/pages/register_page.dart b/mobile/lib/pages/register_page.dart index d7badef..435e550 100644 --- a/mobile/lib/pages/register_page.dart +++ b/mobile/lib/pages/register_page.dart @@ -13,9 +13,9 @@ class RegisterPage extends StatelessWidget { Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - const Text( + Text( "Opret bruger", - style: TextStyle(fontSize: 64), + style: Theme.of(context).textTheme.headlineLarge, ), const PrimaryInput(label: "Fornavn", placeholderText: "Fornavn"), const PrimaryInput( diff --git a/mobile/lib/repos/cart.dart b/mobile/lib/repos/cart.dart new file mode 100644 index 0000000..2da99dc --- /dev/null +++ b/mobile/lib/repos/cart.dart @@ -0,0 +1,105 @@ +import 'package:flutter/material.dart'; +import 'package:mobile/repos/product.dart'; + +class ProductIdException implements Exception {} + +class CartRepo extends ChangeNotifier { + final List cart = [ + CartItem( + product: Product( + id: 1, + name: "Letmælk", + price: 13, + description: "Konventionel letmælk med fedtprocent på 1,5%"), + amount: 1), + CartItem( + product: Product( + id: 2, + name: "Frilands Øko Supermælk", + price: 20, + description: + "Økologisk mælk af frilandskøer med fedtprocent på 3,5%. Ikke homogeniseret eller pasteuriseret. Skaber store muskler og styrker knoglerne 💪"), + amount: 6), + CartItem( + product: Product( + id: 1, + name: "Letmælk", + price: 13, + description: "Konventionel letmælk med fedtprocent på 1,5%"), + amount: 1), + CartItem( + product: Product( + id: 1, + name: "Letmælk", + price: 13, + description: "Konventionel letmælk med fedtprocent på 1,5%"), + amount: 1), + CartItem( + product: Product( + id: 1, + name: "Letmælk", + price: 13, + description: "Konventionel letmælk med fedtprocent på 1,5%"), + amount: 1), + CartItem( + product: Product( + id: 1, + name: "Letmælk", + price: 13, + description: "Konventionel letmælk med fedtprocent på 1,5%"), + amount: 1), + CartItem( + product: Product( + id: 1, + name: "Letmælk", + price: 13, + description: "Konventionel letmælk med fedtprocent på 1,5%"), + amount: 1), + CartItem( + product: Product( + id: 1, + name: "Letmælk", + price: 13, + description: "Konventionel letmælk med fedtprocent på 1,5%"), + amount: 1), + ]; + + List allCartItems() { + return cart; + } + + CartItem withProductId(int productId) { + for (var i = 0; i < cart.length; i++) { + if (cart[i].product.id == productId) { + return cart[i]; + } + } + throw ProductIdException(); + } + + void incrementAmount(int productId) { + var cartItem = withProductId(productId); + cartItem.amount++; + notifyListeners(); + } + + void decrementAmount(int productId) { + var cartItem = withProductId(productId); + if (--cartItem.amount <= 0) { + removeCartItem(cartItem); + } + notifyListeners(); + } + + void removeCartItem(CartItem cartItem) { + cart.remove(cartItem); + notifyListeners(); + } +} + +class CartItem { + final Product product; + int amount; + + CartItem({required this.product, required this.amount}); +} diff --git a/mobile/lib/widgets/primary_card.dart b/mobile/lib/widgets/primary_card.dart new file mode 100644 index 0000000..d5a1d09 --- /dev/null +++ b/mobile/lib/widgets/primary_card.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; + +class PrimaryCard extends StatelessWidget { + final Widget child; + + const PrimaryCard({ + super.key, + required this.child, + }); + + @override + Widget build(BuildContext context) { + return Card( + child: SizedBox(height: 100, child: child), + ); + } +}