calorimeter/lib/food_entry/food_entry_widget.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();
}
}