diff --git a/mobile/analysis_options.yaml b/mobile/analysis_options.yaml index 0d29021..34fdc65 100644 --- a/mobile/analysis_options.yaml +++ b/mobile/analysis_options.yaml @@ -21,6 +21,7 @@ linter: # `// ignore_for_file: name_of_lint` syntax on the line or in the file # producing the lint. rules: + prefer_const_constructors: true # avoid_print: false # Uncomment to disable the `avoid_print` rule # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule diff --git a/mobile/assets/Frilands Øko Supermælk.png b/mobile/assets/Frilands Øko Supermælk.png new file mode 100644 index 0000000..37b15be Binary files /dev/null and b/mobile/assets/Frilands Øko Supermælk.png differ diff --git a/mobile/assets/Letmælk.png b/mobile/assets/Letmælk.png new file mode 100644 index 0000000..63967cc Binary files /dev/null and b/mobile/assets/Letmælk.png differ diff --git a/mobile/assets/Minimælk.png b/mobile/assets/Minimælk.png new file mode 100644 index 0000000..63967cc Binary files /dev/null and b/mobile/assets/Minimælk.png differ diff --git a/mobile/devtools_options.yaml b/mobile/devtools_options.yaml new file mode 100644 index 0000000..fa0b357 --- /dev/null +++ b/mobile/devtools_options.yaml @@ -0,0 +1,3 @@ +description: This file stores settings for Dart & Flutter DevTools. +documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states +extensions: diff --git a/mobile/lib/all_products_page.dart b/mobile/lib/all_products_page.dart new file mode 100644 index 0000000..bbbcee1 --- /dev/null +++ b/mobile/lib/all_products_page.dart @@ -0,0 +1,114 @@ +import 'package:flutter/material.dart'; +import 'package:mobile/product.dart'; +import 'package:provider/provider.dart'; +import 'product_page.dart'; + +class ProductListItem extends StatelessWidget { + final String name; + final int price; + final String imagePath; + final ProductPage productPage; + const ProductListItem( + {super.key, + required this.name, + required this.price, + required this.imagePath, + required this.productPage}); + + @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))), + child: ElevatedButton( + style: ButtonStyle( + backgroundColor: WidgetStateProperty.all(Colors.transparent), + elevation: WidgetStateProperty.all(0), + shape: WidgetStateProperty.all(const RoundedRectangleBorder()), + padding: WidgetStateProperty.all(EdgeInsets.zero), + splashFactory: NoSplash.splashFactory), + onPressed: () { + Navigator.of(context) + .push(MaterialPageRoute(builder: (context) => productPage)); + }, + child: Expanded( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Container( + padding: const EdgeInsets.fromLTRB(10, 10, 0, 10), + child: Column( + 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), + ) + ], + )), + ClipRRect( + borderRadius: const BorderRadius.only( + topRight: Radius.circular(10), + bottomRight: Radius.circular(10)), + child: Image( + image: AssetImage(imagePath), + fit: BoxFit.contain, + width: 100, + )) + ], + ))), + ); + } +} + +class AllProductsPage extends StatelessWidget { + const AllProductsPage({super.key}); + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Expanded( + child: Column(children: [ + const Row( + children: [ + BackButton(), + Expanded( + child: TextField( + decoration: InputDecoration( + label: Text("Search"), + contentPadding: EdgeInsets.only(top: 20))), + ), + ], + ), + Consumer(builder: (_, productRepo, __) { + final products = productRepo.allProducts(); + return ListView.builder( + shrinkWrap: true, + itemBuilder: (_, idx) => ProductListItem( + name: products[idx].name, + price: products[idx].price, + imagePath: "assets/${products[idx].name}.png", + productPage: ProductPage(product: products[idx]), + ), + itemCount: products.length, + ); + }), + ]), + ), + ], + ); + } +} diff --git a/mobile/lib/cart_page.dart b/mobile/lib/cart_page.dart new file mode 100644 index 0000000..3efd557 --- /dev/null +++ b/mobile/lib/cart_page.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +class CartPage extends StatelessWidget { + const CartPage({super.key}); + + @override + Widget build(BuildContext context) { + return const Scaffold(); + } +} diff --git a/mobile/lib/dashboard.dart b/mobile/lib/dashboard.dart index 3398ef6..52aa2b3 100644 --- a/mobile/lib/dashboard.dart +++ b/mobile/lib/dashboard.dart @@ -1,98 +1,67 @@ import 'package:flutter/material.dart'; -import 'global_components.dart'; +import 'package:mobile/all_products_page.dart'; +import 'package:mobile/cart_page.dart'; +import 'package:mobile/receipts_page.dart'; +import 'package:provider/provider.dart'; -class ProductListItem extends StatelessWidget { - final String name; - final int price; - final String imagePath; - const ProductListItem( - {super.key, - required this.name, - required this.price, - required this.imagePath}); +class BottomNavigationBarProvider extends ChangeNotifier { + int currentIndex = 0; - @override - Widget build(BuildContext context) { - return Container( - margin: EdgeInsets.all(10), - height: 100, - decoration: BoxDecoration( - color: Color(0xFFFFFFFF), - border: Border.all(), - borderRadius: BorderRadius.all(Radius.circular(10))), - child: ElevatedButton( - style: ButtonStyle( - backgroundColor: WidgetStateProperty.all(Colors.transparent), - elevation: WidgetStateProperty.all(0), - shape: WidgetStateProperty.all(RoundedRectangleBorder()), - padding: WidgetStateProperty.all(EdgeInsets.zero), - splashFactory: NoSplash.splashFactory), - onPressed: () {}, - child: Expanded( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Container( - padding: EdgeInsets.fromLTRB(10, 10, 0, 10), - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - name, - style: TextStyle(fontSize: 24, color: Colors.black), - ), - Text( - "${price.toString()} kr", - style: TextStyle(fontSize: 16, color: Colors.black), - ) - ], - )), - ClipRRect( - borderRadius: BorderRadius.only( - topRight: Radius.circular(10), - bottomRight: Radius.circular(10)), - child: - Image(image: AssetImage(imagePath), fit: BoxFit.contain)) - ], - ))), - ); + void setIndex(int index) { + currentIndex = index; + notifyListeners(); } } class Dashboard extends StatelessWidget { - const Dashboard({super.key}); + final List pages = [ + const AllProductsPage(), + const CartPage(), + const ReceiptsPage(), + ]; + + Dashboard({super.key}); @override Widget build(BuildContext context) { + final pageIndexProvider = Provider.of(context); + int currentIndex = pageIndexProvider.currentIndex; + return Scaffold( - body: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Expanded( - child: Column(children: [ - TextField( - decoration: InputDecoration( - label: Text("Search"), - contentPadding: EdgeInsets.only(top: 20))), - Expanded( - child: ListView( - children: [ - ProductListItem( - name: "idk", - price: 12, - imagePath: "assets/boykisser.png", - ), - ProductListItem( - name: "idk", - price: 12, - imagePath: "assets/boykisser.png", - ), - ], - ), - ) - ]), - ), - ], - )); + bottomNavigationBar: BottomNavigationBar( + onTap: (index) => pageIndexProvider.setIndex(index), + currentIndex: currentIndex, + items: [ + BottomNavigationBarItem( + icon: Icon(currentIndex == 0 ? Icons.home : Icons.home_outlined), + label: "Home"), + BottomNavigationBarItem( + icon: Icon(currentIndex == 1 + ? Icons.shopping_cart + : Icons.shopping_cart_outlined), + label: "Cart"), + BottomNavigationBarItem( + icon: Icon(currentIndex == 2 + ? Icons.receipt_long + : Icons.receipt_long_outlined), + label: "Receipts") + ], + ), + body: pages[currentIndex], + ); } } + +//Consumer(builder: (_, productRepo, __) { +// final products = productRepo.allProducts(); +// return ListView.builder( +// shrinkWrap: true, +// itemBuilder: (_, idx) => ProductListItem( +// name: products[idx].name, +// price: products[idx].price, +// imagePath: "assets/${products[idx].name}.png", +// productPage: ProductPage(product: products[idx]), +// ), +// itemCount: products.length, +// ); +// }) diff --git a/mobile/lib/log_in_page.dart b/mobile/lib/log_in_page.dart index 88b1cb6..fb174e2 100644 --- a/mobile/lib/log_in_page.dart +++ b/mobile/lib/log_in_page.dart @@ -22,8 +22,8 @@ class LogInPage extends StatelessWidget { label: "Password", placeholderText: "*********", obscure: true), PrimaryButton( onPressed: () => { - Navigator.of(context).push(MaterialPageRoute( - builder: (context) => const Dashboard())) + Navigator.of(context).push( + MaterialPageRoute(builder: (context) => Dashboard())) }, child: const Text("Log ind")) ], diff --git a/mobile/lib/main.dart b/mobile/lib/main.dart index 286baf3..b6d35f2 100644 --- a/mobile/lib/main.dart +++ b/mobile/lib/main.dart @@ -1,4 +1,7 @@ import 'package:flutter/material.dart'; +import 'package:mobile/dashboard.dart'; +import 'package:mobile/product.dart'; +import 'package:provider/provider.dart'; import 'landing_page.dart'; void main() { @@ -10,14 +13,20 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { - return MaterialApp( - title: 'Fresh Plaza', - theme: ThemeData( - colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue), - scaffoldBackgroundColor: const Color(0xECF6F0FF), - useMaterial3: true, + return MultiProvider( + providers: [ + ChangeNotifierProvider(create: (_) => ProductRepo()), + ChangeNotifierProvider(create: (_) => BottomNavigationBarProvider()) + ], + child: MaterialApp( + title: 'Fresh Plaza', + theme: ThemeData( + colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue), + scaffoldBackgroundColor: const Color(0xECF6F0FF), + useMaterial3: true, + ), + home: const MyHomePage(title: 'Fresh Plaza'), ), - home: const MyHomePage(title: 'Fresh Plaza'), ); } } diff --git a/mobile/lib/product.dart b/mobile/lib/product.dart new file mode 100644 index 0000000..2d80715 --- /dev/null +++ b/mobile/lib/product.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; + +class ProductRepo extends ChangeNotifier { + final List _products = [ + Product( + id: 0, + name: "Minimælk", + price: 12, + description: "Konventionel minimælk med fedtprocent på 0,4%"), + Product( + id: 1, + name: "Letmælk", + price: 13, + description: "Konventionel letmælk med fedtprocent på 1,5%"), + 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 💪") + ]; + + List allProducts() { + return _products; + } + + void changePrice(int idx, int price) { + _products[idx].price = price; + notifyListeners(); + } +} + +class Product { + final int id; + final String name; + final String description; + int price; + Product( + {required this.id, + required this.name, + required this.price, + required this.description}); +} diff --git a/mobile/lib/product_page.dart b/mobile/lib/product_page.dart new file mode 100644 index 0000000..ddf2997 --- /dev/null +++ b/mobile/lib/product_page.dart @@ -0,0 +1,83 @@ +import 'package:flutter/material.dart'; +import 'package:mobile/global_components.dart'; +import 'package:mobile/product.dart'; + +class ProductPage extends StatelessWidget { + final Product product; + + const ProductPage({super.key, required this.product}); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Container( + margin: const EdgeInsets.all(10), + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(10)), + color: const Color(0xFFFFFFFF), + border: Border.all(color: const Color(0xFF666666)), + ), + child: Column(children: [ + Row( + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const BackButton(), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + product.name, + style: const TextStyle( + fontSize: 20, + ), + ), + Text( + "${product.price} kr", + style: const TextStyle( + fontSize: 16, + ), + ) + ]) + ], + ), + ], + ), + Expanded( + child: Container( + padding: const EdgeInsets.all(20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Image( + image: AssetImage("assets/${product.name}.png"), + height: 250, + fit: BoxFit.fitHeight, + ), + Text( + product.name, + style: const TextStyle(fontSize: 20), + ), + Text( + "${product.price} kr", + style: const TextStyle(fontSize: 16), + ), + Padding( + padding: const EdgeInsets.only(top: 20, bottom: 20), + child: Text(product.description), + ), + PrimaryButton( + onPressed: () {}, child: const Text("Find i butik")), + PrimaryButton( + onPressed: () {}, + child: const Text("Tilføj til indkøbskurv")), + ], + ), + ), + ) + ]), + ), + ); + } +} diff --git a/mobile/lib/receipts_page.dart b/mobile/lib/receipts_page.dart new file mode 100644 index 0000000..a4a6f66 --- /dev/null +++ b/mobile/lib/receipts_page.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +class ReceiptsPage extends StatelessWidget { + const ReceiptsPage({super.key}); + + @override + Widget build(BuildContext context) { + return const Scaffold(); + } +} diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock index 55f199c..9c587ba 100644 --- a/mobile/pubspec.lock +++ b/mobile/pubspec.lock @@ -131,6 +131,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.15.0" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" path: dependency: transitive description: @@ -139,6 +147,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.0" + provider: + dependency: "direct main" + description: + name: provider + sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c + url: "https://pub.dev" + source: hosted + version: "6.1.2" sky_engine: dependency: transitive description: flutter diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 0653022..02f8c7d 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -34,6 +34,7 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.8 + provider: ^6.1.2 dev_dependencies: flutter_test: