244 lines
7.9 KiB
Dart
244 lines
7.9 KiB
Dart
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
/* Copyright (C) 2024 Marco Groß <mgross@sw-gross.de> */
|
|
import 'package:flutter/material.dart';
|
|
import 'package:calorimeter/food_entry/food_entry_bloc.dart';
|
|
import 'package:calorimeter/utils/row_with_spacers_widget.dart';
|
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
|
|
|
class FoodEntryWidget extends StatefulWidget {
|
|
final FoodEntryState entry;
|
|
final Function(BuildContext context, String id) onDelete;
|
|
final Function(BuildContext context, FoodEntryState entry) onChange;
|
|
final Function(BuildContext context, FoodEntryState entry) onTap;
|
|
|
|
const FoodEntryWidget({
|
|
super.key,
|
|
required this.entry,
|
|
required this.onDelete,
|
|
required this.onChange,
|
|
required this.onTap,
|
|
});
|
|
|
|
@override
|
|
State<FoodEntryWidget> createState() => _FoodEntryWidgetState();
|
|
}
|
|
|
|
class _FoodEntryWidgetState extends State<FoodEntryWidget> {
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return GestureDetector(
|
|
onTap: () => widget.onTap(context, widget.entry),
|
|
child: Stack(
|
|
children: [
|
|
Positioned.fill(
|
|
child: Stack(children: [
|
|
Positioned.fill(
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
|
child: RowWidget(
|
|
widget.entry.waitingForNetwork
|
|
? const Center(child: CircularProgressIndicator())
|
|
: Text(widget.entry.name),
|
|
widget.entry.waitingForNetwork
|
|
? Container()
|
|
: Text(widget.entry.mass.ceil().toString(),
|
|
textAlign: TextAlign.end),
|
|
Opacity(
|
|
opacity: widget.entry.isSelected ? 0.0 : 1.0,
|
|
child: widget.entry.waitingForNetwork
|
|
? Container()
|
|
: Text(widget.entry.kcalPer100.ceil().toString(),
|
|
textAlign: TextAlign.end),
|
|
),
|
|
Opacity(
|
|
opacity: widget.entry.isSelected ? 0.0 : 1.0,
|
|
child: widget.entry.waitingForNetwork
|
|
? Container()
|
|
: Text(
|
|
(widget.entry.mass *
|
|
widget.entry.kcalPer100 /
|
|
100)
|
|
.ceil()
|
|
.toString(),
|
|
textAlign: TextAlign.end),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
Opacity(
|
|
opacity: widget.entry.isSelected ? 0.66 : 0.0,
|
|
child: Container(
|
|
color: Theme.of(context).colorScheme.secondary)),
|
|
]),
|
|
),
|
|
Opacity(
|
|
opacity: widget.entry.isSelected ? 1.0 : 0.0,
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.end,
|
|
children: [
|
|
SizedBox(
|
|
child: IconButton(
|
|
padding: const EdgeInsets.all(0.0),
|
|
icon: const Icon(Icons.edit),
|
|
onPressed: widget.entry.isSelected
|
|
? () async {
|
|
widget.onTap(context, widget.entry);
|
|
await showDialog(
|
|
context: context,
|
|
builder: (dialogContext) {
|
|
return FoodEntryChangeDialog(
|
|
entry: widget.entry,
|
|
onChange: (context, entry) {
|
|
widget.onChange(context, entry);
|
|
});
|
|
},
|
|
);
|
|
}
|
|
: null),
|
|
),
|
|
SizedBox(
|
|
child: IconButton(
|
|
padding: const EdgeInsets.all(0.0),
|
|
iconSize: 24,
|
|
icon: const Icon(Icons.delete),
|
|
color: Colors.redAccent,
|
|
onPressed: widget.entry.isSelected
|
|
? () => widget.onDelete(context, widget.entry.id)
|
|
: null),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class FoodEntryChangeDialog extends StatefulWidget {
|
|
final FoodEntryState entry;
|
|
final Function(BuildContext context, FoodEntryState entry) onChange;
|
|
|
|
const FoodEntryChangeDialog(
|
|
{required this.entry, super.key, required this.onChange});
|
|
|
|
@override
|
|
State<FoodEntryChangeDialog> createState() => _FoodEntryChangeDialogState();
|
|
}
|
|
|
|
class _FoodEntryChangeDialogState extends State<FoodEntryChangeDialog> {
|
|
late TextEditingController nameController;
|
|
late TextEditingController massController;
|
|
late TextEditingController kcalPer100Controller;
|
|
|
|
static const textFieldVerticalPadding = 16.0;
|
|
static const textFieldHorizontalPadding = 16.0;
|
|
|
|
@override
|
|
void initState() {
|
|
nameController = TextEditingController();
|
|
nameController.text = widget.entry.name;
|
|
|
|
massController = TextEditingController();
|
|
massController.text = widget.entry.mass.toString();
|
|
|
|
kcalPer100Controller = TextEditingController();
|
|
kcalPer100Controller.text = widget.entry.kcalPer100.toString();
|
|
|
|
super.initState();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return AlertDialog(
|
|
content: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(
|
|
vertical: textFieldVerticalPadding,
|
|
horizontal: textFieldHorizontalPadding),
|
|
child: TextField(
|
|
onSubmitted: (val) => _onSubmitAction(),
|
|
controller: nameController,
|
|
decoration: InputDecoration(
|
|
label: Text(AppLocalizations.of(context)!.name),
|
|
),
|
|
),
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(
|
|
vertical: textFieldVerticalPadding,
|
|
horizontal: textFieldHorizontalPadding),
|
|
child: TextField(
|
|
onSubmitted: (val) => _onSubmitAction(),
|
|
controller: massController,
|
|
decoration: InputDecoration(
|
|
label: Text(AppLocalizations.of(context)!.amountPer),
|
|
),
|
|
),
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(
|
|
vertical: textFieldVerticalPadding,
|
|
horizontal: textFieldHorizontalPadding),
|
|
child: TextField(
|
|
onSubmitted: (val) => _onSubmitAction(),
|
|
controller: kcalPer100Controller,
|
|
decoration: InputDecoration(
|
|
label: Text(AppLocalizations.of(context)!.kcalper),
|
|
),
|
|
),
|
|
)
|
|
],
|
|
),
|
|
actions: [
|
|
IconButton(
|
|
onPressed: () {
|
|
Navigator.of(context).pop();
|
|
},
|
|
icon: const Icon(Icons.cancel)),
|
|
IconButton(
|
|
onPressed: () => _onSubmitAction(),
|
|
icon: const Icon(Icons.check),
|
|
)
|
|
],
|
|
);
|
|
}
|
|
|
|
void _onSubmitAction() {
|
|
int mass;
|
|
int kcalPer100;
|
|
|
|
try {
|
|
mass = int.parse(massController.text);
|
|
} catch (e) {
|
|
mass = 0;
|
|
}
|
|
|
|
try {
|
|
kcalPer100 = int.parse(kcalPer100Controller.text);
|
|
} catch (e) {
|
|
kcalPer100 = 0;
|
|
}
|
|
|
|
var newEntry = FoodEntryState.withID(
|
|
id: widget.entry.id,
|
|
name: nameController.text,
|
|
mass: mass,
|
|
kcalPer100: kcalPer100,
|
|
waitingForNetwork: widget.entry.waitingForNetwork,
|
|
isSelected: false,
|
|
);
|
|
|
|
widget.onChange(context, newEntry);
|
|
|
|
Navigator.of(context).pop();
|
|
}
|
|
}
|