Remove the card-like design for food entries and make the 'delete' button dynamically appear

This commit is contained in:
Marco 2024-09-06 02:23:47 +02:00
parent 1db4e5e351
commit 6552756702
6 changed files with 150 additions and 91 deletions

View File

@ -32,9 +32,12 @@ class _EnterFoodWidgetState extends State<EnterFoodWidget> {
fieldViewBuilder: (context, controller, focusNode, onSubmitted) { fieldViewBuilder: (context, controller, focusNode, onSubmitted) {
nameController = controller; nameController = controller;
return TextFormField( return TextFormField(
controller: controller, controller: controller,
focusNode: focusNode, focusNode: focusNode,
decoration: const InputDecoration(label: Text("Name"))); decoration: const InputDecoration(
label: Text("Name"),
),
);
}, },
optionsBuilder: (TextEditingValue textEditingValue) { optionsBuilder: (TextEditingValue textEditingValue) {
if (textEditingValue.text == '') { if (textEditingValue.text == '') {
@ -57,63 +60,30 @@ class _EnterFoodWidgetState extends State<EnterFoodWidget> {
}); });
var massWidget = TextField( var massWidget = TextField(
decoration: const InputDecoration(label: Text("Menge")), textAlign: TextAlign.end,
decoration: const InputDecoration(
label: Align(alignment: Alignment.centerRight, child: Text("Menge")),
),
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
controller: massController, controller: massController,
onSubmitted: (value) => onSubmitAction(),
); );
var kcalPerMassWidget = TextField( var kcalPerMassWidget = TextField(
decoration: const InputDecoration(label: Text("kcal pro")), textAlign: TextAlign.end,
keyboardType: TextInputType.number, decoration: const InputDecoration(
controller: kcalPerMassController); label:
Align(alignment: Alignment.centerRight, child: Text("kcal pro"))),
keyboardType: TextInputType.number,
controller: kcalPerMassController,
onSubmitted: (value) => onSubmitAction(),
);
var enterButton = ElevatedButton( var enterButton = ElevatedButton(
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
), ),
onPressed: () { onPressed: () => onSubmitAction(),
double massAsNumber = 0.0;
double kcalPerMassAsNumber = 0.0;
try {
massAsNumber =
double.parse(massController.text.replaceAll(",", "."));
} catch (e) {
var snackbar =
const SnackBar(content: Text("Menge muss eine Zahl sein"));
ScaffoldMessenger.of(context).clearSnackBars();
ScaffoldMessenger.of(context).showSnackBar(snackbar);
return;
}
try {
kcalPerMassAsNumber =
double.parse(kcalPerMassController.text.replaceAll(",", "."));
} catch (e) {
var snackbar = const SnackBar(
content: Text("'kcal pro 100g' muss eine Zahl sein"));
ScaffoldMessenger.of(context).clearSnackBars();
ScaffoldMessenger.of(context).showSnackBar(snackbar);
return;
}
try {
massAsNumber =
double.parse(massController.text.replaceAll(",", "."));
} catch (e) {
var snackbar =
const SnackBar(content: Text("Menge muss eine Zahl sein"));
ScaffoldMessenger.of(context).clearSnackBars();
ScaffoldMessenger.of(context).showSnackBar(snackbar);
return;
}
var entry = FoodEntry(
name: nameController.text,
mass: massAsNumber,
kcalPerMass: kcalPerMassAsNumber);
widget.onAdd(context, entry);
},
child: const Icon(Icons.add)); child: const Icon(Icons.add));
return Padding( return Padding(
@ -122,9 +92,51 @@ class _EnterFoodWidgetState extends State<EnterFoodWidget> {
nameWidget, nameWidget,
massWidget, massWidget,
kcalPerMassWidget, kcalPerMassWidget,
null, Padding(
enterButton, padding: const EdgeInsets.only(left: 16.0),
child: enterButton,
),
), ),
); );
} }
void onSubmitAction() {
double massAsNumber = 0.0;
double kcalPerMassAsNumber = 0.0;
try {
massAsNumber = double.parse(massController.text.replaceAll(",", "."));
} catch (e) {
var snackbar = const SnackBar(content: Text("Menge muss eine Zahl sein"));
ScaffoldMessenger.of(context).clearSnackBars();
ScaffoldMessenger.of(context).showSnackBar(snackbar);
return;
}
try {
kcalPerMassAsNumber =
double.parse(kcalPerMassController.text.replaceAll(",", "."));
} catch (e) {
var snackbar =
const SnackBar(content: Text("'kcal pro 100g' muss eine Zahl sein"));
ScaffoldMessenger.of(context).clearSnackBars();
ScaffoldMessenger.of(context).showSnackBar(snackbar);
return;
}
try {
massAsNumber = double.parse(massController.text.replaceAll(",", "."));
} catch (e) {
var snackbar = const SnackBar(content: Text("Menge muss eine Zahl sein"));
ScaffoldMessenger.of(context).clearSnackBars();
ScaffoldMessenger.of(context).showSnackBar(snackbar);
return;
}
var entry = FoodEntry(
name: nameController.text,
mass: massAsNumber,
kcalPerMass: kcalPerMassAsNumber);
widget.onAdd(context, entry);
}
} }

View File

@ -2,37 +2,93 @@ import 'package:flutter/material.dart';
import 'package:calodiary/food_entry_bloc.dart'; import 'package:calodiary/food_entry_bloc.dart';
import 'package:calodiary/row_with_spacers_widget.dart'; import 'package:calodiary/row_with_spacers_widget.dart';
class FoodEntryWidget extends StatelessWidget { class FoodEntryWidget extends StatefulWidget {
final FoodEntry entry; final FoodEntry entry;
final Function(BuildContext context, String id) onDelete; final Function(BuildContext context, String id) onDelete;
const FoodEntryWidget( const FoodEntryWidget(
{super.key, required this.entry, required this.onDelete}); {super.key, required this.entry, required this.onDelete});
@override
State<FoodEntryWidget> createState() => _FoodEntryWidgetState();
}
class _FoodEntryWidgetState extends State<FoodEntryWidget> {
late bool showCancelAndDelete;
@override
void initState() {
showCancelAndDelete = false;
super.initState();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Dismissible( return GestureDetector(
key: ValueKey(entry.id), onTap: () {
onDismissed: (direction) { setState(() {
onDelete(context, entry.id); showCancelAndDelete = !showCancelAndDelete;
});
}, },
child: Card( child: Stack(
elevation: 5.0, children: [
child: Padding( Positioned.fill(
padding: const EdgeInsets.only(left: 4.0), child: Stack(children: [
child: RowWidget( Positioned.fill(
Text(entry.name), child: Padding(
Text(entry.mass.ceil().toString()), padding: const EdgeInsets.symmetric(horizontal: 8.0),
Text(entry.kcalPerMass.ceil().toString()), child: RowWidget(
Text((entry.mass * entry.kcalPerMass / 100).ceil().toString()), Text(widget.entry.name),
IconButton( Text(widget.entry.mass.ceil().toString(),
style: IconButton.styleFrom(padding: EdgeInsets.zero), textAlign: TextAlign.end),
onPressed: () { Text(widget.entry.kcalPerMass.ceil().toString(),
onDelete(context, entry.id); textAlign: TextAlign.end),
}, Opacity(
icon: const Icon(Icons.delete_forever_rounded)), opacity: showCancelAndDelete ? 0.0 : 1.0,
child: Text(
(widget.entry.mass * widget.entry.kcalPerMass / 100)
.ceil()
.toString(),
textAlign: TextAlign.end),
),
),
),
),
Opacity(
opacity: showCancelAndDelete ? 0.66 : 0.0,
child: Container(
color: Theme.of(context).colorScheme.secondary)),
]),
), ),
), Opacity(
opacity: showCancelAndDelete ? 1.0 : 0.0,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
SizedBox(
child: IconButton(
padding: EdgeInsets.all(0.0),
icon: const Icon(Icons.cancel),
onPressed: showCancelAndDelete
? () => setState(() {
showCancelAndDelete = false;
})
: null,
),
),
SizedBox(
child: IconButton(
padding: EdgeInsets.all(0.0),
iconSize: 24,
icon: const Icon(Icons.delete),
color: Colors.redAccent,
onPressed: showCancelAndDelete
? () => widget.onDelete(context, widget.entry.id)
: null),
),
],
),
),
],
), ),
); );
} }

View File

@ -68,6 +68,7 @@ class MainApp extends StatelessWidget {
Locale('de'), Locale('de'),
], ],
theme: ThemeData( theme: ThemeData(
dividerTheme: const DividerThemeData(space: 2),
colorScheme: ColorScheme.fromSeed( colorScheme: ColorScheme.fromSeed(
seedColor: Colors.lightBlue, seedColor: Colors.lightBlue,
brightness: newBrightness, brightness: newBrightness,

View File

@ -94,6 +94,7 @@ class _PerDateWidgetState extends State<PerDateWidget> {
return Column( return Column(
children: [ children: [
FoodEntryWidget( FoodEntryWidget(
key: ValueKey(state.foodEntries[entryIndex].id),
entry: state.foodEntries[entryIndex], entry: state.foodEntries[entryIndex],
onDelete: (callbackContext, id) { onDelete: (callbackContext, id) {
callbackContext callbackContext
@ -103,8 +104,7 @@ class _PerDateWidgetState extends State<PerDateWidget> {
)); ));
}, },
), ),
if (listIndex != state.foodEntries.length - 1) const Divider(),
const Divider(),
], ],
); );
}, },

View File

@ -1,31 +1,22 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class RowWidget extends StatefulWidget { class RowWidget extends StatelessWidget {
final Widget? widget1; final Widget? widget1;
final Widget? widget2; final Widget? widget2;
final Widget? widget3; final Widget? widget3;
final Widget? widget4; final Widget? widget4;
final Widget? widget5;
const RowWidget( const RowWidget(this.widget1, this.widget2, this.widget3, this.widget4,
this.widget1, this.widget2, this.widget3, this.widget4, this.widget5,
{super.key}); {super.key});
@override
State<RowWidget> createState() => _RowWidgetState();
}
class _RowWidgetState extends State<RowWidget> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Row( return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Expanded(flex: 10, child: widget.widget1 ?? Container()), Expanded(flex: 10, child: widget1 ?? Container()),
Expanded(flex: 6, child: widget.widget2 ?? Container()), Expanded(flex: 6, child: widget2 ?? Container()),
Expanded(flex: 6, child: widget.widget3 ?? Container()), Expanded(flex: 6, child: widget3 ?? Container()),
Expanded(flex: 8, child: widget.widget4 ?? Container()), Expanded(flex: 6, child: widget4 ?? Container()),
Expanded(flex: 3, child: widget.widget5 ?? Container()),
], ],
); );
} }

View File

@ -33,7 +33,6 @@ class _SumWidgetState extends State<SumWidget> {
null, null,
null, null,
null, null,
null,
); );
}, },
); );