2024-12-07 12:29:34 +00:00
|
|
|
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
2024-12-07 12:39:11 +00:00
|
|
|
/* Copyright (C) 2024 Marco Groß <mgross@sw-gross.de> */
|
2025-01-03 21:34:01 +00:00
|
|
|
|
2024-05-29 22:58:26 +00:00
|
|
|
import 'package:flutter/material.dart';
|
2024-09-06 17:00:25 +00:00
|
|
|
import 'package:calorimeter/food_entry/food_entry_bloc.dart';
|
2024-09-06 16:51:24 +00:00
|
|
|
import 'package:calorimeter/utils/row_with_spacers_widget.dart';
|
2024-12-10 21:37:13 +00:00
|
|
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
2024-05-29 22:58:26 +00:00
|
|
|
|
|
|
|
class EnterFoodWidget extends StatefulWidget {
|
2024-09-09 20:41:48 +00:00
|
|
|
final Function(BuildContext context, FoodEntryState entry) onAdd;
|
2025-01-03 21:34:01 +00:00
|
|
|
final Map<String, int> foodEntryLookupDatabase;
|
2024-06-09 17:06:10 +00:00
|
|
|
|
2025-01-03 21:34:01 +00:00
|
|
|
const EnterFoodWidget(
|
|
|
|
{super.key, required this.onAdd, required this.foodEntryLookupDatabase});
|
2024-05-29 22:58:26 +00:00
|
|
|
|
|
|
|
@override
|
|
|
|
State<EnterFoodWidget> createState() => _EnterFoodWidgetState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class _EnterFoodWidgetState extends State<EnterFoodWidget> {
|
2024-09-06 22:02:01 +00:00
|
|
|
late TextEditingController nameController;
|
|
|
|
late TextEditingController massController;
|
|
|
|
late TextEditingController kcalPerMassController;
|
2025-01-03 21:34:01 +00:00
|
|
|
late bool open;
|
2024-09-04 20:47:32 +00:00
|
|
|
|
|
|
|
@override
|
|
|
|
void initState() {
|
2024-09-06 22:02:01 +00:00
|
|
|
nameController = TextEditingController();
|
|
|
|
massController = TextEditingController();
|
|
|
|
kcalPerMassController = TextEditingController();
|
2025-01-03 21:34:01 +00:00
|
|
|
|
|
|
|
open = false;
|
2024-09-06 22:02:01 +00:00
|
|
|
|
2024-09-04 20:47:32 +00:00
|
|
|
super.initState();
|
|
|
|
}
|
2024-06-01 10:41:58 +00:00
|
|
|
|
2024-05-29 22:58:26 +00:00
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
2025-01-03 21:34:01 +00:00
|
|
|
return Column(
|
|
|
|
children: [
|
|
|
|
Stack(
|
|
|
|
children: [
|
|
|
|
if (!open)
|
|
|
|
RowWidget(
|
|
|
|
showDividers: false,
|
|
|
|
null,
|
|
|
|
null,
|
|
|
|
null,
|
|
|
|
ElevatedButton(
|
|
|
|
style: ElevatedButton.styleFrom(
|
|
|
|
padding: EdgeInsets.zero,
|
|
|
|
),
|
|
|
|
onPressed: () => setState(() => open = true),
|
|
|
|
child: Icon(Icons.add)),
|
|
|
|
),
|
|
|
|
Offstage(
|
|
|
|
offstage: !open,
|
|
|
|
child: AnimatedOpacity(
|
|
|
|
duration: Duration(milliseconds: 250),
|
|
|
|
opacity: open ? 1.0 : 0.0,
|
|
|
|
child: RowWidget(
|
|
|
|
showDividers: true,
|
|
|
|
Autocomplete<String>(
|
|
|
|
optionsViewOpenDirection: OptionsViewOpenDirection.down,
|
|
|
|
fieldViewBuilder:
|
|
|
|
(context, controller, focusNode, onSubmitted) {
|
|
|
|
nameController = controller;
|
|
|
|
return TextFormField(
|
|
|
|
scrollPadding: EdgeInsets.only(bottom: 100),
|
|
|
|
controller: controller,
|
|
|
|
focusNode: focusNode,
|
|
|
|
decoration: InputDecoration(
|
|
|
|
label: Text(AppLocalizations.of(context)!.name),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
},
|
|
|
|
optionsBuilder: (TextEditingValue textEditingValue) {
|
|
|
|
if (textEditingValue.text == '') {
|
|
|
|
return const Iterable<String>.empty();
|
|
|
|
}
|
2024-09-06 22:02:01 +00:00
|
|
|
|
2025-01-03 21:34:01 +00:00
|
|
|
return widget.foodEntryLookupDatabase.keys.where(
|
|
|
|
(name) {
|
|
|
|
return name
|
|
|
|
.toLowerCase()
|
|
|
|
.contains(textEditingValue.text.toLowerCase());
|
|
|
|
},
|
|
|
|
);
|
|
|
|
},
|
|
|
|
onSelected: (selectedFood) {
|
|
|
|
int kcalPerMassForSelectedFood =
|
|
|
|
widget.foodEntryLookupDatabase[selectedFood]!;
|
|
|
|
setState(() {
|
|
|
|
nameController.text = selectedFood;
|
|
|
|
kcalPerMassController.text =
|
|
|
|
kcalPerMassForSelectedFood.toString();
|
|
|
|
});
|
|
|
|
}),
|
|
|
|
TextField(
|
|
|
|
scrollPadding: EdgeInsets.only(bottom: 100),
|
|
|
|
textAlign: TextAlign.end,
|
|
|
|
decoration: InputDecoration(
|
|
|
|
label: Directionality(
|
|
|
|
textDirection: TextDirection.rtl,
|
|
|
|
child: Text(AppLocalizations.of(context)!.amount),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
keyboardType: TextInputType.number,
|
|
|
|
controller: massController,
|
|
|
|
onSubmitted: (value) => onSubmitAction(),
|
|
|
|
),
|
|
|
|
TextField(
|
|
|
|
scrollPadding: EdgeInsets.only(bottom: 100),
|
|
|
|
textAlign: TextAlign.end,
|
|
|
|
decoration: InputDecoration(
|
|
|
|
label: Directionality(
|
|
|
|
textDirection: TextDirection.rtl,
|
|
|
|
child: Text(AppLocalizations.of(context)!.kcal))),
|
|
|
|
keyboardType: TextInputType.number,
|
|
|
|
controller: kcalPerMassController,
|
|
|
|
onSubmitted: (value) => onSubmitAction(),
|
|
|
|
),
|
|
|
|
ElevatedButton(
|
|
|
|
style: ElevatedButton.styleFrom(
|
|
|
|
padding: EdgeInsets.zero,
|
|
|
|
),
|
|
|
|
onPressed: () => onSubmitAction(),
|
|
|
|
child: const Icon(Icons.check)),
|
|
|
|
),
|
2024-09-09 20:41:48 +00:00
|
|
|
),
|
2025-01-03 21:34:01 +00:00
|
|
|
),
|
|
|
|
],
|
2024-09-09 20:41:48 +00:00
|
|
|
),
|
2025-01-03 21:34:01 +00:00
|
|
|
SizedBox(
|
|
|
|
height: 200,
|
|
|
|
child: GestureDetector(
|
|
|
|
onTap: () => setState(() {
|
|
|
|
open = false;
|
|
|
|
}))),
|
|
|
|
],
|
2024-05-29 22:58:26 +00:00
|
|
|
);
|
|
|
|
}
|
2024-09-06 00:23:47 +00:00
|
|
|
|
|
|
|
void onSubmitAction() {
|
2024-09-09 20:41:48 +00:00
|
|
|
int massAsNumber = 0;
|
|
|
|
int kcalPerMassAsNumber = 0;
|
2024-09-06 00:23:47 +00:00
|
|
|
|
|
|
|
try {
|
2024-09-09 20:41:48 +00:00
|
|
|
massAsNumber = int.parse(massController.text.replaceAll(",", "."));
|
2024-09-06 00:23:47 +00:00
|
|
|
} catch (e) {
|
2024-12-10 21:37:13 +00:00
|
|
|
var snackbar = SnackBar(
|
|
|
|
content: Text(AppLocalizations.of(context)!.errAmountNotANumber));
|
2024-09-06 00:23:47 +00:00
|
|
|
ScaffoldMessenger.of(context).clearSnackBars();
|
|
|
|
ScaffoldMessenger.of(context).showSnackBar(snackbar);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
kcalPerMassAsNumber =
|
2024-09-09 20:41:48 +00:00
|
|
|
int.parse(kcalPerMassController.text.replaceAll(",", "."));
|
2024-09-06 00:23:47 +00:00
|
|
|
} catch (e) {
|
2024-12-10 21:37:13 +00:00
|
|
|
var snackbar = SnackBar(
|
|
|
|
content: Text(AppLocalizations.of(context)!.errKcalNotANumber));
|
2024-09-09 20:41:48 +00:00
|
|
|
ScaffoldMessenger.of(context).removeCurrentSnackBar();
|
2024-09-06 00:23:47 +00:00
|
|
|
ScaffoldMessenger.of(context).showSnackBar(snackbar);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-09-09 20:41:48 +00:00
|
|
|
var entry = FoodEntryState(
|
|
|
|
name: nameController.text,
|
|
|
|
mass: massAsNumber,
|
2024-09-24 15:23:01 +00:00
|
|
|
kcalPer100: kcalPerMassAsNumber,
|
2024-09-09 20:41:48 +00:00
|
|
|
waitingForNetwork: false,
|
2024-09-25 15:33:40 +00:00
|
|
|
isSelected: false,
|
2024-09-09 20:41:48 +00:00
|
|
|
);
|
2024-09-06 00:23:47 +00:00
|
|
|
|
|
|
|
widget.onAdd(context, entry);
|
2024-09-09 20:41:48 +00:00
|
|
|
setState(() {
|
|
|
|
nameController.text = "";
|
|
|
|
massController.text = "";
|
|
|
|
kcalPerMassController.text = "";
|
2025-01-03 21:34:01 +00:00
|
|
|
open = false;
|
2024-09-09 20:41:48 +00:00
|
|
|
});
|
2024-09-06 00:23:47 +00:00
|
|
|
}
|
2024-05-29 22:58:26 +00:00
|
|
|
}
|