Many ui changes. Add some navigation

This commit is contained in:
Marco 2024-06-09 14:42:17 +02:00
parent a5afda8de6
commit 2298c12704
12 changed files with 375 additions and 161 deletions

3
devtools_options.yaml Normal file
View File

@ -0,0 +1,3 @@
description: This file stores settings for Dart & Flutter DevTools.
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
extensions:

45
lib/app_drawer.dart Normal file
View File

@ -0,0 +1,45 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:kalodings/calendar.dart';
class AppDrawer extends StatelessWidget {
const AppDrawer({
super.key,
});
@override
Widget build(BuildContext context) {
return Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: [
DrawerHeader(
decoration: const BoxDecoration(
color: Colors.redAccent,
),
child: ListTile(
leading: IconButton(
icon: const Icon(Icons.close),
onPressed: () {
Navigator.pop(context);
},
),
title: const Text('Menü')),
),
ListTile(
trailing: const Icon(Icons.calendar_month),
title: const Text('Kalender'),
onTap: () {
context.goNamed('calendar');
},
),
ListTile(
title: const Text('Einstellungen'),
trailing: const Icon(Icons.settings),
onTap: () {},
),
],
),
);
}
}

20
lib/calendar.dart Normal file
View File

@ -0,0 +1,20 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
class CalendarWidget extends StatelessWidget {
const CalendarWidget({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: CalendarDatePicker(
onDateChanged: (value) {
context.goNamed('perDay', extra: value);
},
initialDate: DateTime.now(),
firstDate: DateTime.now().subtract(const Duration(days: 30)),
lastDate: DateTime.now(),
),
);
}
}

View File

@ -1,6 +1,9 @@
import 'dart:developer';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:kalodings/food_entry_bloc.dart';
import 'package:kalodings/row_with_spacers_widget.dart';
class EnterFoodWidget extends StatefulWidget {
const EnterFoodWidget({
@ -32,95 +35,67 @@ class _EnterFoodWidgetState extends State<EnterFoodWidget> {
var kcalPerMassWidget = TextField(
decoration: const InputDecoration(hintText: "kcal pro 100g"),
keyboardType: TextInputType.number,
controller: kcalPerMassController);
var enterButton = ElevatedButton(
onPressed: () {
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);
log('Entry id: ${entry.id}');
context.read<FoodEntryBloc>().add(FoodEntryEvent(entry: entry));
},
child: const Icon(Icons.add));
return Column(
children: [
RowWidgetWithSpacers(
nameWidget,
massWidget,
kcalPerMassWidget,
),
Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton(
child: const Text("Hinzufügen"),
onPressed: () {
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,
kcal: kcalPerMassAsNumber,
kcalPerMass: 200);
context.read<FoodEntryBloc>().add(FoodEntryEvent(entry: entry));
}),
null,
enterButton,
),
],
);
}
}
class RowWidgetWithSpacers extends StatefulWidget {
final Widget widget1;
final Widget widget2;
final Widget widget3;
const RowWidgetWithSpacers(this.widget1, this.widget2, this.widget3,
{super.key});
@override
State<RowWidgetWithSpacers> createState() => _RowWidgetWithSpacersState();
}
class _RowWidgetWithSpacersState extends State<RowWidgetWithSpacers> {
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
const Spacer(flex: 1),
Expanded(flex: 4, child: widget.widget1),
Expanded(
flex: 4,
child: widget.widget2,
),
Expanded(flex: 4, child: widget.widget3),
const Spacer(flex: 1),
],
);
}
}

View File

@ -1,9 +1,10 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:quiver/core.dart';
import 'package:uuid/uuid.dart';
class FoodEntryBloc extends Bloc<FoodEntryEvent, FoodEntryState> {
class FoodEntryBloc extends Bloc<FoodEvent, FoodEntryState> {
FoodEntryBloc(super.initialState) {
on<FoodEntryEvent>(addFoodEntryToState);
on<FoodDeletionEvent>(deleteFood);
}
void addFoodEntryToState(FoodEntryEvent event, Emitter<FoodEntryState> emit) {
@ -11,13 +12,25 @@ class FoodEntryBloc extends Bloc<FoodEntryEvent, FoodEntryState> {
newState.addEntry(event.entry);
emit(newState);
}
void deleteFood(FoodDeletionEvent event, Emitter<FoodEntryState> emit) {
state.foodEntries.removeWhere((entry) => entry.id == event.entryID);
emit(FoodEntryState.from(state));
}
}
class FoodEntryEvent {
class FoodEvent {}
class FoodEntryEvent extends FoodEvent {
final FoodEntry entry;
FoodEntryEvent({required this.entry});
}
class FoodDeletionEvent extends FoodEvent {
final String entryID;
FoodDeletionEvent({required this.entryID});
}
class FoodEntryState {
final List<FoodEntry> foodEntries;
@ -35,42 +48,17 @@ class FoodEntryState {
void addEntry(FoodEntry entry) {
foodEntries.add(entry);
}
@override
int get hashCode {
return hashObjects(foodEntries);
}
@override
operator ==(other) {
if (other.runtimeType != FoodEntryState) return false;
other = other as FoodEntryState;
if (foodEntries.length != other.foodEntries.length) return false;
for (var i = 0; i < foodEntries.length; i += 1) {
if (foodEntries[i].name != other.foodEntries[i].name ||
foodEntries[i].mass != other.foodEntries[i].mass ||
foodEntries[i].kcalPerMass != other.foodEntries[i].kcalPerMass ||
foodEntries[i].kcal != other.foodEntries[i].kcal) {
return false;
}
}
return true;
}
}
class FoodEntry {
final String name;
final double mass;
final double kcalPerMass;
final double kcal;
final String id;
FoodEntry({
required this.name,
required this.mass,
required this.kcalPerMass,
required this.kcal,
});
}) : id = const Uuid().v1();
}

View File

@ -1,23 +1,29 @@
import 'package:flutter/material.dart';
import 'package:kalodings/enter_food_widget.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:kalodings/food_entry_bloc.dart';
import 'package:kalodings/row_with_spacers_widget.dart';
class FoodEntryWidget extends StatelessWidget {
final String name;
final double mass;
final double kcalPerMass;
final FoodEntry entry;
const FoodEntryWidget(
{super.key,
required this.name,
required this.mass,
required this.kcalPerMass});
const FoodEntryWidget({super.key, required this.entry});
@override
Widget build(BuildContext context) {
return RowWidgetWithSpacers(
Text(name),
Text(mass.toString()),
Text(kcalPerMass.toString()),
return Card(
child: RowWidgetWithSpacers(
Text(entry.name),
Text(entry.mass.toString()),
Text(entry.kcalPerMass.toString()),
Text((entry.mass * entry.kcalPerMass / 100).toString()),
IconButton(
onPressed: () {
context
.read<FoodEntryBloc>()
.add(FoodDeletionEvent(entryID: entry.id));
},
icon: const Icon(Icons.delete_forever_rounded)),
),
);
}
}

View File

@ -1,5 +1,9 @@
// ignore_for_file: prefer_const_constructors
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import 'package:kalodings/calendar.dart';
import 'package:kalodings/food_entry_bloc.dart';
import 'package:kalodings/perday_widget.dart';
@ -16,13 +20,38 @@ class MainApp extends StatelessWidget {
create: (BuildContext context) {
return FoodEntryBloc(FoodEntryState.init());
},
child: const MaterialApp(
home: Scaffold(
body: Center(
child: PerDay(),
),
),
child: MaterialApp.router(
theme: ThemeData.dark(),
routerConfig: router,
),
);
}
}
final router = GoRouter(routes: [
GoRoute(
path: '/',
name: 'perDayToday',
builder: (context, state) {
return PerDay(DateTime.now());
}),
GoRoute(
path: '/day',
name: 'perDay',
builder: (context, state) {
DateTime day;
if (state.extra == null || state.extra is! DateTime) {
day = DateTime.now();
} else {
day = state.extra as DateTime;
}
return PerDay(day);
}),
GoRoute(
path: '/calendar',
name: 'calendar',
builder: (context, state) {
return CalendarWidget();
}),
]);

View File

@ -1,43 +1,38 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:kalodings/app_drawer.dart';
import 'package:kalodings/enter_food_widget.dart';
import 'package:kalodings/food_entry_bloc.dart';
import 'package:kalodings/food_entry_widget.dart';
import 'package:kalodings/sum_widget.dart';
class PerDay extends StatefulWidget {
const PerDay({super.key});
@override
State<PerDay> createState() => _PerDayState();
}
class _PerDayState extends State<PerDay> {
String perFoodresult = "Kalorien der Mahlzeit";
List<FoodEntryWidget> entryList = [];
class PerDay extends StatelessWidget {
final DateTime day;
const PerDay(this.day, {super.key});
@override
Widget build(BuildContext context) {
return BlocListener<FoodEntryBloc, FoodEntryState>(
listener: (listenerContext, state) {
var newEntryWidgets = <FoodEntryWidget>[];
return Scaffold(
appBar: AppBar(
title: Text(day.toString()),
),
drawer: const AppDrawer(),
body: BlocBuilder<FoodEntryBloc, FoodEntryState>(
builder: (context, state) {
return ListView.builder(
itemCount: state.foodEntries.length + 2,
itemBuilder: (BuildContext itemBuilderContext, int index) {
if (index == state.foodEntries.length) {
return const SumWidget();
}
if (index == state.foodEntries.length + 1) {
return const EnterFoodWidget();
}
for (var entry in state.foodEntries) {
newEntryWidgets.add(FoodEntryWidget(
name: entry.name,
mass: entry.mass,
kcalPerMass: entry.kcalPerMass));
}
setState(() {
entryList = newEntryWidgets;
});
},
child: ListView.builder(
itemCount: entryList.length + 1,
itemBuilder: (BuildContext itemBuilderContext, int index) {
if (index + 1 == entryList.length + 1) return EnterFoodWidget();
return entryList[index];
},
),
);
return FoodEntryWidget(entry: state.foodEntries[index]);
},
);
},
));
}
}

View File

@ -0,0 +1,62 @@
import 'package:flutter/material.dart';
class RowWidgetWithSpacers extends StatefulWidget {
final Widget? widget1;
final Widget? widget2;
final Widget? widget3;
final Widget? widget4;
final Widget? widget5;
const RowWidgetWithSpacers(
this.widget1, this.widget2, this.widget3, this.widget4, this.widget5,
{super.key});
@override
State<RowWidgetWithSpacers> createState() => _RowWidgetWithSpacersState();
}
class _RowWidgetWithSpacersState extends State<RowWidgetWithSpacers> {
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Expanded(
flex: 5,
child: Padding(
padding:
const EdgeInsets.symmetric(vertical: 2.0, horizontal: 2.0),
child: widget.widget1 ?? Container(),
)),
Expanded(
flex: 5,
child: Padding(
padding:
const EdgeInsets.symmetric(vertical: 2.0, horizontal: 2.0),
child: widget.widget2 ?? Container(),
)),
Expanded(
flex: 5,
child: Padding(
padding:
const EdgeInsets.symmetric(vertical: 2.0, horizontal: 2.0),
child: widget.widget3 ?? Container(),
)),
Expanded(
flex: 5,
child: Padding(
padding:
const EdgeInsets.symmetric(vertical: 2.0, horizontal: 2.0),
child: widget.widget4 ?? Container(),
)),
Expanded(
flex: 5,
child: Padding(
padding:
const EdgeInsets.symmetric(vertical: 2.0, horizontal: 2.0),
child: widget.widget5 ?? Container(),
)),
],
);
}
}

28
lib/sum_widget.dart Normal file
View File

@ -0,0 +1,28 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:kalodings/food_entry_bloc.dart';
import 'package:kalodings/row_with_spacers_widget.dart';
class SumWidget extends StatelessWidget {
const SumWidget({super.key});
@override
Widget build(BuildContext context) {
return BlocBuilder<FoodEntryBloc, FoodEntryState>(
builder: (context, state) {
var sum = 0.0;
for (var entry in state.foodEntries) {
sum += entry.kcalPerMass / 100 * entry.mass;
}
return RowWidgetWithSpacers(
null,
null,
const Text("kcal heute:"),
Text(sum.toString()),
null,
);
},
);
}
}

View File

@ -49,6 +49,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.18.0"
crypto:
dependency: transitive
description:
name: crypto
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
url: "https://pub.dev"
source: hosted
version: "3.0.3"
fake_async:
dependency: transitive
description:
@ -57,6 +65,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.1"
fixnum:
dependency: transitive
description:
name: fixnum
sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
flutter:
dependency: "direct main"
description: flutter
@ -83,6 +99,19 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_web_plugins:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
go_router:
dependency: "direct main"
description:
name: go_router
sha256: abec47eb8c8c36ebf41d0a4c64dbbe7f956e39a012b3aafc530e951bdc12fe3f
url: "https://pub.dev"
source: hosted
version: "14.1.4"
leak_tracker:
dependency: transitive
description:
@ -115,6 +144,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.0.0"
logging:
dependency: transitive
description:
name: logging
sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340"
url: "https://pub.dev"
source: hosted
version: "1.2.0"
matcher:
dependency: transitive
description:
@ -184,6 +221,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.10.0"
sprintf:
dependency: transitive
description:
name: sprintf
sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23"
url: "https://pub.dev"
source: hosted
version: "7.0.0"
stack_trace:
dependency: transitive
description:
@ -224,6 +269,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.7.1"
typed_data:
dependency: transitive
description:
name: typed_data
sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
url: "https://pub.dev"
source: hosted
version: "1.3.2"
uuid:
dependency: "direct main"
description:
name: uuid
sha256: "814e9e88f21a176ae1359149021870e87f7cddaf633ab678a5d2b0bff7fd1ba8"
url: "https://pub.dev"
source: hosted
version: "4.4.0"
vector_math:
dependency: transitive
description:
@ -241,5 +302,5 @@ packages:
source: hosted
version: "14.2.2"
sdks:
dart: ">=3.5.0-191.0.dev <4.0.0"
dart: ">=3.3.0 <4.0.0"
flutter: ">=3.18.0-18.0.pre.54"

View File

@ -4,13 +4,15 @@ publish_to: 'none'
version: 0.1.0
environment:
sdk: '>=3.5.0-191.0.dev <4.0.0'
sdk: ^3.0.0
dependencies:
flutter:
sdk: flutter
flutter_bloc: ^8.1.5
go_router: ^14.1.4
quiver: ^3.2.1
uuid: ^4.4.0
dev_dependencies:
flutter_test: