Remove the card-like design for food entries and make the 'delete' button dynamically appear
This commit is contained in:
parent
1db4e5e351
commit
6552756702
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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(),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -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()),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,6 @@ class _SumWidgetState extends State<SumWidget> {
|
|||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null,
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user