diff --git a/lib/client.dart b/lib/client.dart index 3efc0cc..b52c2d7 100644 --- a/lib/client.dart +++ b/lib/client.dart @@ -219,4 +219,19 @@ class ApiClient { "order": order, }); } + + Future> addProduct({ + required String name, + required double protein, + required double carb, + required double fat, + }) async { + var response = await post("/product", { + "name": name, + "protein": protein, + "carb": carb, + "fat": fat, + }); + return response; + } } diff --git a/lib/screens/add_entry.dart b/lib/screens/add_entry.dart index 338ec2a..5420943 100644 --- a/lib/screens/add_entry.dart +++ b/lib/screens/add_entry.dart @@ -5,6 +5,7 @@ import 'package:fooder_web/models/product.dart'; import 'package:fooder_web/models/diary.dart'; import 'package:fooder_web/models/meal.dart'; import 'package:fooder_web/widgets/product.dart'; +import 'package:fooder_web/screens/add_product.dart'; class AddEntryScreen extends BasedScreen { @@ -132,6 +133,27 @@ class _AddEntryScreen extends State { controller: productNameController, onChanged: (_) => _getProducts(), ), + TextButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => AddProductScreen( + apiClient: widget.apiClient, + ), + ), + ).then((product) { + if (product == null) { + return; + } + setState(() { + products = [product]; + productNameController.text = product.name; + }); + }); + }, + child: const Text("Don't see your product? Add it!"), + ), for (var product in products) ListTile( onTap: () { diff --git a/lib/screens/add_product.dart b/lib/screens/add_product.dart new file mode 100644 index 0000000..c68fe72 --- /dev/null +++ b/lib/screens/add_product.dart @@ -0,0 +1,180 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:fooder_web/screens/based.dart'; +import 'package:fooder_web/models/product.dart'; + + +class AddProductScreen extends BasedScreen { + const AddProductScreen({super.key, required super.apiClient}); + + @override + State createState() => _AddProductScreen(); +} + + +class _AddProductScreen extends State { + final nameController = TextEditingController(); + final carbController = TextEditingController(); + final fatController = TextEditingController(); + final proteinController = TextEditingController(); + + @override + void dispose() { + nameController.dispose(); + carbController.dispose(); + fatController.dispose(); + proteinController.dispose(); + super.dispose(); + } + + void popMeDady(Product product) { + Navigator.pop( + context, + product, + ); + } + + void showError(String message) + { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(message, textAlign: TextAlign.center), + backgroundColor: Theme.of(context).colorScheme.error, + ), + ); + } + + Future _addProduct() async { + try { + double.parse(carbController.text); + } catch (e) { + showError("Carbs must be a number"); + return; + } + + try { + double.parse(fatController.text); + } catch (e) { + showError("Fat must be a number"); + return; + } + + try { + double.parse(proteinController.text); + } catch (e) { + showError("Protein must be a number"); + return; + } + + try { + var productJson = await widget.apiClient.addProduct( + carb: double.parse(carbController.text), + fat: double.parse(fatController.text), + protein: double.parse(proteinController.text), + name: nameController.text, + ); + var product = Product.fromJson(productJson); + popMeDady(product); + } catch (e) { + showError("Error adding product, make sure there is no product with the same name"); + return; + } + } + + double calculateCalories() { + double calories = 0; + + try { + calories += double.parse(carbController.text) * 4; + } catch (e) { + // ignore + } + + try { + calories += double.parse(fatController.text) * 9; + } catch (e) { + // ignore + } + + try { + calories += double.parse(proteinController.text) * 4; + } catch (e) { + // ignore + } + + return calories; + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + backgroundColor: Theme.of(context).colorScheme.inversePrimary, + title: const Text("πŸ…΅πŸ…ΎπŸ…ΎπŸ…³πŸ…΄πŸ†"), + ), + body: Center( + child: Container( + constraints: const BoxConstraints(maxWidth: 720), + padding: const EdgeInsets.all(10), + child: Column( + children: [ + TextFormField( + decoration: const InputDecoration( + labelText: 'Product name', + ), + controller: nameController, + ), + TextFormField( + decoration: const InputDecoration( + labelText: 'Carbs', + ), + keyboardType:const TextInputType.numberWithOptions(decimal: true), + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'^(\d+)?\.?\d{0,2}')), + ], + controller: carbController, + onChanged: (String value) { + setState(() {}); + }, + ), + TextFormField( + decoration: const InputDecoration( + labelText: 'Fat', + ), + keyboardType:const TextInputType.numberWithOptions(decimal: true), + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'^(\d+)?\.?\d{0,2}')), + ], + controller: fatController, + onChanged: (String value) { + setState(() {}); + }, + ), + TextFormField( + decoration: const InputDecoration( + labelText: 'Protein', + ), + keyboardType:const TextInputType.numberWithOptions(decimal: true), + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'^(\d+)?\.?\d{0,2}')), + ], + controller: proteinController, + onChanged: (String value) { + setState(() {}); + }, + ), + Text( + "${calculateCalories()} kcal", + textAlign: TextAlign.right, + ), + ] + ) + ), + ), + floatingActionButton: FloatingActionButton( + onPressed: _addProduct, + child: const Icon(Icons.add), + ), + ); + } +} diff --git a/lib/screens/edit_entry.dart b/lib/screens/edit_entry.dart index 84ba57e..db27510 100644 --- a/lib/screens/edit_entry.dart +++ b/lib/screens/edit_entry.dart @@ -4,7 +4,7 @@ import 'package:fooder_web/screens/based.dart'; import 'package:fooder_web/models/product.dart'; import 'package:fooder_web/models/entry.dart'; import 'package:fooder_web/widgets/product.dart'; -import 'package:fooder_web/models/meal.dart'; +import 'package:fooder_web/screens/add_product.dart'; class EditEntryScreen extends BasedScreen { @@ -118,6 +118,27 @@ class _EditEntryScreen extends State { controller: productNameController, onChanged: (_) => _getProducts(), ), + TextButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => AddProductScreen( + apiClient: widget.apiClient, + ), + ), + ).then((product) { + if (product == null) { + return; + } + setState(() { + products = [product]; + productNameController.text = product.name; + }); + }); + }, + child: const Text("Don't see your product? Add it!"), + ), for (var product in products) ListTile( onTap: () {