added editing entries and deleting them
This commit is contained in:
parent
fa9a0569fb
commit
955c506b92
9 changed files with 369 additions and 101 deletions
|
@ -76,7 +76,7 @@ class ApiClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Future<Map<String, dynamic>> delete(String path) async {
|
Future<void> delete(String path) async {
|
||||||
final response = await httpClient.delete(
|
final response = await httpClient.delete(
|
||||||
Uri.parse('$baseUrl$path'),
|
Uri.parse('$baseUrl$path'),
|
||||||
headers: headers(),
|
headers: headers(),
|
||||||
|
@ -85,8 +85,6 @@ class ApiClient {
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
throw Exception('Response returned status code: ${response.statusCode}');
|
throw Exception('Response returned status code: ${response.statusCode}');
|
||||||
}
|
}
|
||||||
|
|
||||||
return _jsonDecode(response);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Map<String, dynamic>> patch(String path, Map<String, dynamic> body) async {
|
Future<Map<String, dynamic>> patch(String path, Map<String, dynamic> body) async {
|
||||||
|
@ -182,4 +180,22 @@ class ApiClient {
|
||||||
};
|
};
|
||||||
await post("/entry", entry);
|
await post("/entry", entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> deleteEntry(int id) async {
|
||||||
|
await delete("/entry/$id");
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> updateEntry(int id, {
|
||||||
|
required double grams,
|
||||||
|
required int productId,
|
||||||
|
required int mealId,
|
||||||
|
}
|
||||||
|
) async {
|
||||||
|
var entry = {
|
||||||
|
"grams": grams,
|
||||||
|
"product_id": productId,
|
||||||
|
"meal_id": mealId,
|
||||||
|
};
|
||||||
|
await patch("/entry/$id", entry);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,7 +65,6 @@ class _AddEntryScreen extends State<AddEntryScreen> {
|
||||||
try {
|
try {
|
||||||
double.parse(gramsController.text);
|
double.parse(gramsController.text);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e);
|
|
||||||
showError("Grams must be a number");
|
showError("Grams must be a number");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
146
lib/screens/edit_entry.dart
Normal file
146
lib/screens/edit_entry.dart
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
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';
|
||||||
|
|
||||||
|
|
||||||
|
class EditEntryScreen extends BasedScreen {
|
||||||
|
final Entry entry;
|
||||||
|
|
||||||
|
const EditEntryScreen({super.key, required super.apiClient, required this.entry});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<EditEntryScreen> createState() => _EditEntryScreen();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class _EditEntryScreen extends State<EditEntryScreen> {
|
||||||
|
final gramsController = TextEditingController();
|
||||||
|
final productNameController = TextEditingController();
|
||||||
|
List<Product> products = [];
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
gramsController.dispose();
|
||||||
|
productNameController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void popMeDady() {
|
||||||
|
Navigator.pop(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState () {
|
||||||
|
super.initState();
|
||||||
|
setState(() {
|
||||||
|
gramsController.text = widget.entry.grams.toString();
|
||||||
|
productNameController.text = widget.entry.product.name;
|
||||||
|
products = [widget.entry.product];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _getProducts() async {
|
||||||
|
var productsMap = await widget.apiClient.getProducts(productNameController.text);
|
||||||
|
setState(() {
|
||||||
|
products = (productsMap['products'] as List<dynamic>).map((e) => Product.fromJson(e as Map<String, dynamic>)).toList();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void showError(String message)
|
||||||
|
{
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text(message, textAlign: TextAlign.center),
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.error,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _saveEntry() async {
|
||||||
|
if (products.length != 1) {
|
||||||
|
showError("Pick product first");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
double.parse(gramsController.text);
|
||||||
|
} catch (e) {
|
||||||
|
showError("Grams must be a number");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await widget.apiClient.updateEntry(
|
||||||
|
widget.entry.id,
|
||||||
|
grams: double.parse(gramsController.text),
|
||||||
|
productId: products[0].id,
|
||||||
|
mealId: widget.entry.mealId,
|
||||||
|
);
|
||||||
|
popMeDady();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _deleteEntry() async {
|
||||||
|
await widget.apiClient.deleteEntry(widget.entry.id);
|
||||||
|
popMeDady();
|
||||||
|
}
|
||||||
|
|
||||||
|
@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: ListView(
|
||||||
|
children: <Widget>[
|
||||||
|
TextFormField(
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: 'Grams',
|
||||||
|
),
|
||||||
|
controller: gramsController,
|
||||||
|
),
|
||||||
|
TextFormField(
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: 'Product name',
|
||||||
|
),
|
||||||
|
controller: productNameController,
|
||||||
|
onChanged: (_) => _getProducts(),
|
||||||
|
),
|
||||||
|
for (var product in products)
|
||||||
|
ListTile(
|
||||||
|
onTap: () {
|
||||||
|
setState(() {
|
||||||
|
products = [product];
|
||||||
|
});
|
||||||
|
},
|
||||||
|
title: ProductWidget(
|
||||||
|
product: product,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
floatingActionButton: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: <Widget>[
|
||||||
|
FloatingActionButton(
|
||||||
|
onPressed: _deleteEntry,
|
||||||
|
heroTag: null,
|
||||||
|
child: const Icon(Icons.delete),
|
||||||
|
),
|
||||||
|
FloatingActionButton(
|
||||||
|
onPressed: _saveEntry,
|
||||||
|
heroTag: null,
|
||||||
|
child: const Icon(Icons.save),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -69,7 +69,7 @@ class _MainScreen extends State<MainScreen> {
|
||||||
content = Container(
|
content = Container(
|
||||||
constraints: const BoxConstraints(maxWidth: 720),
|
constraints: const BoxConstraints(maxWidth: 720),
|
||||||
padding: const EdgeInsets.all(10),
|
padding: const EdgeInsets.all(10),
|
||||||
child: DiaryWidget(diary: diary!),
|
child: DiaryWidget(diary: diary!, apiClient: widget.apiClient, refreshParent: _asyncInitState),
|
||||||
);
|
);
|
||||||
title = Row(
|
title = Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
|
|
@ -1,55 +1,67 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:fooder_web/models/diary.dart';
|
import 'package:fooder_web/models/diary.dart';
|
||||||
import 'package:fooder_web/widgets/meal.dart';
|
import 'package:fooder_web/widgets/meal.dart';
|
||||||
|
import 'package:fooder_web/widgets/macro.dart';
|
||||||
|
import 'package:fooder_web/client.dart';
|
||||||
import 'dart:core';
|
import 'dart:core';
|
||||||
|
|
||||||
|
|
||||||
class DiaryWidget extends StatelessWidget {
|
class DiaryWidget extends StatelessWidget {
|
||||||
final Diary diary;
|
final Diary diary;
|
||||||
|
final ApiClient apiClient;
|
||||||
|
final Function() refreshParent;
|
||||||
|
|
||||||
const DiaryWidget({super.key, required this.diary});
|
const DiaryWidget({super.key, required this.diary, required this.apiClient, required this.refreshParent});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
padding: const EdgeInsets.all(8),
|
padding: const EdgeInsets.all(8),
|
||||||
child: ListView(
|
child: CustomScrollView(
|
||||||
padding: const EdgeInsets.all(8),
|
slivers: <Widget>[
|
||||||
|
SliverAppBar(
|
||||||
|
title: Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
|
const Spacer(),
|
||||||
|
Text(
|
||||||
|
"${diary.calories.toStringAsFixed(1)} kcal",
|
||||||
|
style: Theme.of(context).textTheme.headlineLarge!.copyWith(
|
||||||
|
color: Theme.of(context).colorScheme.onSecondary,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
expandedHeight: 128,
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.secondary,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
floating: true,
|
||||||
|
flexibleSpace: FlexibleSpaceBar(
|
||||||
|
title: MacroWidget(
|
||||||
|
protein: diary.protein,
|
||||||
|
carb: diary.carb,
|
||||||
|
fat: diary.fat,
|
||||||
|
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||||
|
color: Theme.of(context).colorScheme.onSecondary,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SliverList(
|
||||||
|
delegate: SliverChildListDelegate(
|
||||||
|
[
|
||||||
for (var meal in diary.meals)
|
for (var meal in diary.meals)
|
||||||
MealWidget(
|
MealWidget(
|
||||||
meal: meal,
|
meal: meal,
|
||||||
),
|
apiClient: apiClient,
|
||||||
Card(
|
refreshParent: refreshParent,
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(8.0),
|
|
||||||
child: Column(
|
|
||||||
children: <Widget>[
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: <Widget>[
|
|
||||||
Text(
|
|
||||||
"carb: ${diary.carb.toStringAsFixed(2)}",
|
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.secondary),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
"fat: ${diary.fat.toStringAsFixed(2)}",
|
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.secondary),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
"protein: ${diary.protein.toStringAsFixed(2)}",
|
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.secondary),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
"calories: ${diary.calories.toStringAsFixed(2)}",
|
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.primary),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:fooder_web/models/entry.dart';
|
import 'package:fooder_web/models/entry.dart';
|
||||||
|
import 'package:fooder_web/widgets/macro.dart';
|
||||||
import 'dart:core';
|
import 'dart:core';
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,29 +23,17 @@ class EntryWidget extends StatelessWidget {
|
||||||
style: Theme.of(context).textTheme.titleLarge,
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text("${entry.calories.toStringAsFixed(2)} kcal"),
|
Text("${entry.calories.toStringAsFixed(1)} kcal"),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Row(
|
MacroWidget(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
protein: entry.protein,
|
||||||
children: <Widget>[
|
carb: entry.carb,
|
||||||
Text(
|
fat: entry.fat,
|
||||||
"carb: ${entry.carb.toStringAsFixed(2)}",
|
amount: entry.grams,
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.secondary),
|
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||||
|
color: Theme.of(context).colorScheme.secondary,
|
||||||
),
|
),
|
||||||
Text(
|
|
||||||
"fat: ${entry.fat.toStringAsFixed(2)}",
|
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.secondary),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
"protein: ${entry.protein.toStringAsFixed(2)}",
|
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.secondary),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
"amount: ${entry.grams.toStringAsFixed(2)}",
|
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.secondary),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
102
lib/widgets/macro.dart
Normal file
102
lib/widgets/macro.dart
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
|
||||||
|
class MacroWidget extends StatelessWidget {
|
||||||
|
final double? amount;
|
||||||
|
final double? calories;
|
||||||
|
final double protein;
|
||||||
|
final double carb;
|
||||||
|
final double fat;
|
||||||
|
final TextStyle style;
|
||||||
|
|
||||||
|
const MacroWidget({
|
||||||
|
Key? key,
|
||||||
|
this.calories,
|
||||||
|
this.amount,
|
||||||
|
required this.protein,
|
||||||
|
required this.carb,
|
||||||
|
required this.fat,
|
||||||
|
required this.style,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
var elements = <Widget>[
|
||||||
|
Expanded(
|
||||||
|
flex: 1,
|
||||||
|
child: Text(
|
||||||
|
"C: ${carb.toStringAsFixed(1)} g",
|
||||||
|
style: style,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
flex: 1,
|
||||||
|
child: Text(
|
||||||
|
"F: ${fat.toStringAsFixed(1)} g",
|
||||||
|
style: style,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
flex: 1,
|
||||||
|
child: Text(
|
||||||
|
"P: ${protein.toStringAsFixed(1)} g",
|
||||||
|
style: style,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
if (calories != null) {
|
||||||
|
elements.add(
|
||||||
|
Expanded(
|
||||||
|
flex: 1,
|
||||||
|
child: Text(
|
||||||
|
"${calories!.toStringAsFixed(1)} kcal",
|
||||||
|
style: style,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (amount != null) {
|
||||||
|
elements.add(
|
||||||
|
Expanded(
|
||||||
|
flex: 1,
|
||||||
|
child: Text(
|
||||||
|
"${amount!.toStringAsFixed(1)} g",
|
||||||
|
style: style,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (amount == null && calories == null) {
|
||||||
|
elements.add(
|
||||||
|
Expanded(
|
||||||
|
flex: 1,
|
||||||
|
child: Text(
|
||||||
|
"",
|
||||||
|
style: style,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
top: 4.0,
|
||||||
|
bottom: 4.0,
|
||||||
|
left: 8.0,
|
||||||
|
right: 8.0,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: elements,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,13 +1,18 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:fooder_web/models/meal.dart';
|
import 'package:fooder_web/models/meal.dart';
|
||||||
import 'package:fooder_web/widgets/entry.dart';
|
import 'package:fooder_web/widgets/entry.dart';
|
||||||
|
import 'package:fooder_web/widgets/macro.dart';
|
||||||
|
import 'package:fooder_web/screens/edit_entry.dart';
|
||||||
|
import 'package:fooder_web/client.dart';
|
||||||
import 'dart:core';
|
import 'dart:core';
|
||||||
|
|
||||||
|
|
||||||
class MealWidget extends StatelessWidget {
|
class MealWidget extends StatelessWidget {
|
||||||
final Meal meal;
|
final Meal meal;
|
||||||
|
final ApiClient apiClient;
|
||||||
|
final Function() refreshParent;
|
||||||
|
|
||||||
const MealWidget({super.key, required this.meal});
|
const MealWidget({super.key, required this.meal, required this.apiClient, required this.refreshParent});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -26,33 +31,39 @@ class MealWidget extends StatelessWidget {
|
||||||
style: Theme.of(context).textTheme.titleLarge,
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text("${meal.calories.toStringAsFixed(2)} kcal"),
|
Text("${meal.calories.toStringAsFixed(1)} kcal"),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Row(
|
MacroWidget(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
protein: meal.protein,
|
||||||
children: <Widget>[
|
carb: meal.carb,
|
||||||
Text(
|
fat: meal.fat,
|
||||||
"carb: ${meal.carb.toStringAsFixed(2)}",
|
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.secondary),
|
color: Theme.of(context).colorScheme.secondary,
|
||||||
),
|
),
|
||||||
Text(
|
|
||||||
"fat: ${meal.fat.toStringAsFixed(2)}",
|
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.secondary),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
"protein: ${meal.protein.toStringAsFixed(2)}",
|
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.secondary),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
for (var entry in meal.entries)
|
for (var entry in meal.entries)
|
||||||
EntryWidget(
|
ListTile(
|
||||||
|
title: EntryWidget(
|
||||||
entry: entry,
|
entry: entry,
|
||||||
),
|
),
|
||||||
|
onTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => EditEntryScreen(
|
||||||
|
apiClient: apiClient,
|
||||||
|
entry: entry,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
).then((_) {
|
||||||
|
refreshParent();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:fooder_web/models/product.dart';
|
import 'package:fooder_web/models/product.dart';
|
||||||
|
import 'package:fooder_web/widgets/macro.dart';
|
||||||
import 'dart:core';
|
import 'dart:core';
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,25 +23,17 @@ class ProductWidget extends StatelessWidget {
|
||||||
style: Theme.of(context).textTheme.titleLarge,
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text("${product.calories.toStringAsFixed(2)} kcal"),
|
Text("${product.calories.toStringAsFixed(1)} kcal"),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Row(
|
MacroWidget(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
protein: product.protein,
|
||||||
children: <Widget>[
|
carb: product.carb,
|
||||||
Text(
|
fat: product.fat,
|
||||||
"carb: ${product.carb.toStringAsFixed(2)}",
|
style: Theme.of(context).textTheme.bodyMedium!.copyWith(
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.secondary),
|
color: Theme.of(context).colorScheme.secondary,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
Text(
|
|
||||||
"fat: ${product.fat.toStringAsFixed(2)}",
|
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.secondary),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
"protein: ${product.protein.toStringAsFixed(2)}",
|
|
||||||
style: TextStyle(color: Theme.of(context).colorScheme.secondary),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
Loading…
Reference in a new issue