bottom navigation bar and product page

This commit is contained in:
Mikkel Troels Kongsted 2025-01-28 14:30:42 +01:00
parent f5d9507427
commit 66a8f6c7aa
15 changed files with 353 additions and 94 deletions

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 625 KiB

BIN
mobile/assets/Letmælk.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

BIN
mobile/assets/Minimælk.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

View File

@ -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:

View File

@ -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<ProductRepo>(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,
);
}),
]),
),
],
);
}
}

10
mobile/lib/cart_page.dart Normal file
View File

@ -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();
}
}

View File

@ -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<StatelessWidget> pages = [
const AllProductsPage(),
const CartPage(),
const ReceiptsPage(),
];
Dashboard({super.key});
@override
Widget build(BuildContext context) {
final pageIndexProvider = Provider.of<BottomNavigationBarProvider>(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>[
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<ProductRepo>(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,
// );
// })

View File

@ -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"))
],

View File

@ -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'),
);
}
}

43
mobile/lib/product.dart Normal file
View File

@ -0,0 +1,43 @@
import 'package:flutter/material.dart';
class ProductRepo extends ChangeNotifier {
final List<Product> _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<Product> 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});
}

View File

@ -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")),
],
),
),
)
]),
),
);
}
}

View File

@ -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();
}
}

View File

@ -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

View File

@ -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: