use product repo by server

This commit is contained in:
Mikkel Troels Kongsted 2025-03-06 08:28:53 +01:00
parent 2ce894d756
commit 850391045e
13 changed files with 116 additions and 60 deletions

View File

@ -43,4 +43,5 @@
</intent> </intent>
</queries> </queries>
<uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.INTERNET"/>
</manifest> </manifest>

View File

@ -23,7 +23,7 @@ class MyApp extends StatelessWidget {
return MultiProvider( return MultiProvider(
providers: [ providers: [
ChangeNotifierProvider(create: (_) => Routing()), ChangeNotifierProvider(create: (_) => Routing()),
ChangeNotifierProvider(create: (_) => ProductRepoByMemory()), ChangeNotifierProvider(create: (_) => ProductRepo()),
ChangeNotifierProvider(create: (_) => CartRepo()), ChangeNotifierProvider(create: (_) => CartRepo()),
ChangeNotifierProvider(create: (_) => ReceiptRepo()), ChangeNotifierProvider(create: (_) => ReceiptRepo()),
ChangeNotifierProvider(create: (_) => PayingStateRepo()), ChangeNotifierProvider(create: (_) => PayingStateRepo()),

View File

@ -4,16 +4,24 @@ class Product {
final int id; final int id;
final String name; final String name;
final String description; final String description;
final int priceInDkkCent; final int priceInDkkCents;
final Coordinate? location; final Coordinate? location;
final String? barcode; final String? barcode;
Product({ Product({
required this.id, required this.id,
required this.name, required this.name,
required this.priceInDkkCent, required this.priceInDkkCents,
required this.description, required this.description,
this.location, this.location,
this.barcode, this.barcode,
}); });
Product.fromJson(Map<String, dynamic> json)
: id = json["id"],
name = json["name"],
description = json["description"],
priceInDkkCents = json["priceInDkkCents"],
location = null,
barcode = null;
} }

View File

@ -99,7 +99,7 @@ class AllProductsPage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final productRepo = Provider.of<ProductRepoByMemory>(context); final productRepo = Provider.of<ProductRepo>(context);
return Row( return Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
@ -122,15 +122,14 @@ class AllProductsPage extends StatelessWidget {
], ],
), ),
Expanded( Expanded(
child: child: Consumer<ProductRepo>(builder: (_, productRepo, __) {
Consumer<ProductRepoByMemory>(builder: (_, productRepo, __) {
final products = productRepo.filteredProducts; final products = productRepo.filteredProducts;
return ListView.builder( return ListView.builder(
shrinkWrap: true, shrinkWrap: true,
itemBuilder: (_, idx) => ProductListItem( itemBuilder: (_, idx) => ProductListItem(
productId: products[idx].id, productId: products[idx].id,
name: products[idx].name, name: products[idx].name,
price: products[idx].priceInDkkCent, price: products[idx].priceInDkkCents,
productPage: ProductPage(product: products[idx]), productPage: ProductPage(product: products[idx]),
product: products[idx], product: products[idx],
), ),

View File

@ -162,7 +162,7 @@ class CartPage extends StatelessWidget {
cartRepo: cartRepo, cartRepo: cartRepo,
productId: cart[idx].product.id, productId: cart[idx].product.id,
name: cart[idx].product.name, name: cart[idx].product.name,
price: cart[idx].product.priceInDkkCent, price: cart[idx].product.priceInDkkCents,
amount: cart[idx].amount), amount: cart[idx].amount),
itemCount: cart.length, itemCount: cart.length,
); );
@ -205,9 +205,8 @@ class CartPage extends StatelessWidget {
child: const Text("Cancel")), child: const Text("Cancel")),
TextButton( TextButton(
onPressed: () { onPressed: () {
final ProductRepoByMemory final productRepo =
productRepo = context.read< context.read<ProductRepo>();
ProductRepoByMemory>();
final CartRepo cartRepo = final CartRepo cartRepo =
context.read<CartRepo>(); context.read<CartRepo>();
final productResult = final productResult =
@ -271,8 +270,8 @@ class CartPage extends StatelessWidget {
} }
final CartRepo cartRepo = final CartRepo cartRepo =
context.read<CartRepo>(); context.read<CartRepo>();
final ProductRepoByMemory productRepo = final productRepo =
context.read<ProductRepoByMemory>(); context.read<ProductRepo>();
final productResult = productRepo final productResult = productRepo
.productWithBarcode(result.rawContent); .productWithBarcode(result.rawContent);
switch (productResult) { switch (productResult) {

View File

@ -36,7 +36,7 @@ class FinishShoppingPage extends StatelessWidget {
child: ListView.builder( child: ListView.builder(
shrinkWrap: true, shrinkWrap: true,
itemBuilder: (_, idx) => ReceiptItemView( itemBuilder: (_, idx) => ReceiptItemView(
pricePerAmount: cart[idx].product.priceInDkkCent, pricePerAmount: cart[idx].product.priceInDkkCents,
name: cart[idx].product.name, name: cart[idx].product.name,
amount: cart[idx].amount), amount: cart[idx].amount),
itemCount: cart.length)), itemCount: cart.length)),

View File

@ -39,7 +39,7 @@ class ProductPage extends StatelessWidget {
), ),
), ),
Text( Text(
formatDkkCents(product.priceInDkkCent), formatDkkCents(product.priceInDkkCents),
style: const TextStyle( style: const TextStyle(
fontSize: 16, fontSize: 16,
), ),
@ -67,7 +67,7 @@ class ProductPage extends StatelessWidget {
style: Theme.of(context).textTheme.bodyLarge, style: Theme.of(context).textTheme.bodyLarge,
), ),
Text( Text(
formatDkkCents(product.priceInDkkCent), formatDkkCents(product.priceInDkkCents),
style: Theme.of(context).textTheme.bodyLarge, style: Theme.of(context).textTheme.bodyLarge,
), ),
Padding( Padding(

View File

@ -29,7 +29,7 @@ class ReceiptView extends StatelessWidget {
shrinkWrap: true, shrinkWrap: true,
itemBuilder: (_, idx) => ReceiptItemView( itemBuilder: (_, idx) => ReceiptItemView(
pricePerAmount: pricePerAmount:
receiptItems[idx].product.priceInDkkCent, receiptItems[idx].product.priceInDkkCents,
name: receiptItems[idx].product.name, name: receiptItems[idx].product.name,
amount: receiptItems[idx].amount), amount: receiptItems[idx].amount),
itemCount: receiptItems.length), itemCount: receiptItems.length),

View File

@ -9,14 +9,14 @@ class CartRepo extends ChangeNotifier {
product: Product( product: Product(
id: 1, id: 1,
name: "Letmælk", name: "Letmælk",
priceInDkkCent: 1295, priceInDkkCents: 1295,
description: "Konventionel letmælk med fedtprocent på 1,5%"), description: "Konventionel letmælk med fedtprocent på 1,5%"),
amount: 1), amount: 1),
CartItem( CartItem(
product: Product( product: Product(
id: 2, id: 2,
name: "Frilands Øko Supermælk", name: "Frilands Øko Supermælk",
priceInDkkCent: 1995, priceInDkkCents: 1995,
description: description:
"Økologisk mælk af frilandskøer med fedtprocent på 3,5%. Ikke homogeniseret eller pasteuriseret. Skaber store muskler og styrker knoglerne 💪"), "Økologisk mælk af frilandskøer med fedtprocent på 3,5%. Ikke homogeniseret eller pasteuriseret. Skaber store muskler og styrker knoglerne 💪"),
amount: 6), amount: 6),
@ -24,7 +24,7 @@ class CartRepo extends ChangeNotifier {
product: Product( product: Product(
id: 3, id: 3,
name: "Minimælk", name: "Minimælk",
priceInDkkCent: 1195, priceInDkkCents: 1195,
description: "Konventionel minimælk med fedtprocent på 0,4%"), description: "Konventionel minimælk med fedtprocent på 0,4%"),
amount: 1), amount: 1),
]; ];
@ -98,7 +98,7 @@ class CartRepo extends ChangeNotifier {
return cart.fold<int>( return cart.fold<int>(
0, 0,
(prev, cartItem) => (prev, cartItem) =>
prev + cartItem.amount * cartItem.product.priceInDkkCent); prev + cartItem.amount * cartItem.product.priceInDkkCents);
} }
void clearCart() { void clearCart() {

View File

@ -1,30 +1,28 @@
import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:mobile/models/coordinate.dart'; import 'package:mobile/models/coordinate.dart';
import 'package:mobile/models/product.dart'; import 'package:mobile/models/product.dart';
import 'package:mobile/results.dart'; import 'package:mobile/results.dart';
class ProductRepoByMemory extends ChangeNotifier { class ProductRepo extends ChangeNotifier {
int _nextId = 0; int _nextId = 0;
List<Product> products = []; List<Product> products = [];
late List<Product> filteredProducts; String query = "";
ProductRepoByMemory() { ProductRepo() {
_addAllProducts(); _addAllProducts();
filteredProducts = products;
} }
int getNextId() { int getNextId() {
return _nextId++; return _nextId++;
} }
List<Product> allProducts() { get filteredProducts {
if (query.trim().isEmpty) {
return products; return products;
} }
return products.where((product) {
void searchProducts(String query) {
if (query.trim().isEmpty) {
filteredProducts = products;
} else {
filteredProducts = products.where((product) {
final nameLower = product.name.toLowerCase(); final nameLower = product.name.toLowerCase();
final descriptionLower = product.description.toLowerCase(); final descriptionLower = product.description.toLowerCase();
final searchLower = query.toLowerCase(); final searchLower = query.toLowerCase();
@ -33,6 +31,9 @@ class ProductRepoByMemory extends ChangeNotifier {
descriptionLower.contains(searchLower); descriptionLower.contains(searchLower);
}).toList(); }).toList();
} }
void searchProducts(String query) {
this.query = query;
notifyListeners(); notifyListeners();
} }
@ -50,78 +51,125 @@ class ProductRepoByMemory extends ChangeNotifier {
Product( Product(
id: _nextId++, id: _nextId++,
name: "Minimælk", name: "Minimælk",
priceInDkkCent: 1200, priceInDkkCents: 1200,
description: "Konventionel minimælk med fedtprocent på 0,4%"), description: "Konventionel minimælk med fedtprocent på 0,4%"),
Product( Product(
id: _nextId++, id: _nextId++,
name: "Letmælk", name: "Letmælk",
priceInDkkCent: 1300, priceInDkkCents: 1300,
description: "Konventionel letmælk med fedtprocent på 1,5%", description: "Konventionel letmælk med fedtprocent på 1,5%",
location: Coordinate(x: 1800, y: 100)), location: Coordinate(x: 1800, y: 100)),
Product( Product(
id: _nextId++, id: _nextId++,
name: "Frilands Øko Supermælk", name: "Frilands Øko Supermælk",
priceInDkkCent: 2000, priceInDkkCents: 2000,
description: description:
"Økologisk mælk af frilandskøer med fedtprocent på 3,5%. Ikke homogeniseret eller pasteuriseret. Skaber store muskler og styrker knoglerne 💪"), "Økologisk mælk af frilandskøer med fedtprocent på 3,5%. Ikke homogeniseret eller pasteuriseret. Skaber store muskler og styrker knoglerne 💪"),
Product( Product(
id: _nextId++, id: _nextId++,
name: "Øko Gulerødder 1 kg", name: "Øko Gulerødder 1 kg",
priceInDkkCent: 1000, priceInDkkCents: 1000,
description: ""), description: ""),
Product( Product(
id: _nextId++, id: _nextId++,
name: "Øko Agurk", name: "Øko Agurk",
priceInDkkCent: 1000, priceInDkkCents: 1000,
description: ""), description: ""),
Product( Product(
id: _nextId++, id: _nextId++,
name: "Æbler 1 kg", name: "Æbler 1 kg",
priceInDkkCent: 1000, priceInDkkCents: 1000,
description: ""), description: ""),
Product( Product(
id: _nextId++, id: _nextId++,
name: "Basmati Ris", name: "Basmati Ris",
priceInDkkCent: 2000, priceInDkkCents: 2000,
description: ""), description: ""),
Product( Product(
id: _nextId++, id: _nextId++,
name: "Haribo Mix", name: "Haribo Mix",
priceInDkkCent: 3000, priceInDkkCents: 3000,
description: ""), description: ""),
Product( Product(
id: _nextId++, name: "Smør", priceInDkkCent: 3000, description: ""), id: _nextId++, name: "Smør", priceInDkkCents: 3000, description: ""),
Product( Product(
id: _nextId++, id: _nextId++,
name: "Harboe Cola", name: "Harboe Cola",
priceInDkkCent: 500, priceInDkkCents: 500,
description: ""), description: ""),
Product( Product(
id: _nextId++, id: _nextId++,
name: "Monster Energi Drik", name: "Monster Energi Drik",
priceInDkkCent: 2000, priceInDkkCents: 2000,
description: ""), description: ""),
Product( Product(
id: _nextId++, id: _nextId++,
name: "Spaghetti", name: "Spaghetti",
priceInDkkCent: 1000, priceInDkkCents: 1000,
description: ""), description: ""),
Product( Product(
id: _nextId++, id: _nextId++,
name: "Rød Cecil", name: "Rød Cecil",
priceInDkkCent: 6000, priceInDkkCents: 6000,
description: ""), description: ""),
Product( Product(
id: _nextId++, id: _nextId++,
name: "Jägermeister 750 ml", name: "Jägermeister 750 ml",
priceInDkkCent: 12000, priceInDkkCents: 12000,
description: ""), description: ""),
Product( Product(
id: _nextId++, id: _nextId++,
barcode: "5711953068881", barcode: "5711953068881",
name: "Protein Chokoladedrik", name: "Protein Chokoladedrik",
priceInDkkCent: 1500, priceInDkkCents: 1500,
description: "Arla's protein chokolade drik der giver store muskler"), description: "Arla's protein chokolade drik der giver store muskler"),
]; ];
} }
} }
class ProductRepoByServer extends ChangeNotifier {
String apiUrl = "http://127.0.0.1:8080/products.json";
List<Product> products = [];
String query = "";
ProductRepoByServer() {
fetchProductsFromServer();
}
Future<void> fetchProductsFromServer() async {
final res = await http.get(
Uri.parse(apiUrl),
);
final productsJson = List<Map<String, dynamic>>.from(jsonDecode(res.body));
products =
productsJson.map(((product) => Product.fromJson(product))).toList();
notifyListeners();
}
get filteredProducts {
if (query.trim().isEmpty) {
return products;
}
return products.where((product) {
final nameLower = product.name.toLowerCase();
final descriptionLower = product.description.toLowerCase();
final searchLower = query.toLowerCase();
return nameLower.contains(searchLower) ||
descriptionLower.contains(searchLower);
}).toList();
}
void searchProducts(String query) {
this.query = query;
notifyListeners();
}
Result<Product, String> productWithBarcode(String barcode) {
for (var i = 0; i < products.length; i++) {
if (products[i].barcode == barcode) {
return Ok(products[i]);
}
}
return Err("Product with barcode $barcode doesn't exist");
}
}

View File

@ -13,14 +13,14 @@ class ReceiptRepo extends ChangeNotifier {
product: Product( product: Product(
id: 1243, id: 1243,
name: "Letmælk", name: "Letmælk",
priceInDkkCent: 13, priceInDkkCents: 13,
description: "Konventionel minimælk med fedtprocent på 0,4%"), description: "Konventionel minimælk med fedtprocent på 0,4%"),
amount: 1), amount: 1),
ReceiptItem( ReceiptItem(
product: Product( product: Product(
id: 340, id: 340,
name: "Minimælk", name: "Minimælk",
priceInDkkCent: 12, priceInDkkCents: 12,
description: "Konventionel minimælk med fedtprocent på 0,4%"), description: "Konventionel minimælk med fedtprocent på 0,4%"),
amount: 3), amount: 3),
]), ]),
@ -32,14 +32,14 @@ class ReceiptRepo extends ChangeNotifier {
product: Product( product: Product(
id: 12341, id: 12341,
name: "Letmælk", name: "Letmælk",
priceInDkkCent: 13, priceInDkkCents: 13,
description: "Konventionel minimælk med fedtprocent på 0,4%"), description: "Konventionel minimælk med fedtprocent på 0,4%"),
amount: 3), amount: 3),
ReceiptItem( ReceiptItem(
product: Product( product: Product(
id: 1234443, id: 1234443,
name: "Minimælk", name: "Minimælk",
priceInDkkCent: 12, priceInDkkCents: 12,
description: "Konventionel minimælk med fedtprocent på 0,4%"), description: "Konventionel minimælk med fedtprocent på 0,4%"),
amount: 1), amount: 1),
]) ])
@ -119,6 +119,6 @@ class ReceiptItem {
ReceiptItem({required this.product, required this.amount}); ReceiptItem({required this.product, required this.amount});
int totalPrice() { int totalPrice() {
return product.priceInDkkCent * amount; return product.priceInDkkCents * amount;
} }
} }

View File

@ -116,7 +116,7 @@ packages:
source: hosted source: hosted
version: "6.2.1" version: "6.2.1"
http: http:
dependency: transitive dependency: "direct main"
description: description:
name: http name: http
sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f

View File

@ -38,6 +38,7 @@ dependencies:
barcode_scan2: ^4.4.0 barcode_scan2: ^4.4.0
google_fonts: ^6.2.1 google_fonts: ^6.2.1
intl: ^0.20.2 intl: ^0.20.2
http: ^1.3.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: