From 7a6062fd95b559dc9c4ec408a10f69a56ebb9216 Mon Sep 17 00:00:00 2001 From: Marco Date: Mon, 10 Jun 2024 03:06:56 +0200 Subject: [PATCH] Introduce settings for daily kcal limit --- lib/app_drawer.dart | 5 +- lib/calendar_floating_button.dart | 27 ++++++++++ lib/food_entry_bloc.dart | 25 +++++++-- lib/main.dart | 8 +++ lib/perdate_widget.dart | 84 +++++++++++++------------------ lib/settings.dart | 66 ++++++++++++++++++++++++ lib/storage/storage.dart | 33 ++++++++++++ lib/sum_widget.dart | 6 ++- pubspec.lock | 8 +++ pubspec.yaml | 1 + 10 files changed, 207 insertions(+), 56 deletions(-) create mode 100644 lib/calendar_floating_button.dart create mode 100644 lib/settings.dart diff --git a/lib/app_drawer.dart b/lib/app_drawer.dart index 1ede28c..1be7621 100644 --- a/lib/app_drawer.dart +++ b/lib/app_drawer.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; class AppDrawer extends StatelessWidget { const AppDrawer({ @@ -27,7 +28,9 @@ class AppDrawer extends StatelessWidget { ListTile( title: const Text('Einstellungen'), trailing: const Icon(Icons.settings), - onTap: () {}, + onTap: () { + context.goNamed('settings'); + }, ), ], ), diff --git a/lib/calendar_floating_button.dart b/lib/calendar_floating_button.dart new file mode 100644 index 0000000..0f0ac6f --- /dev/null +++ b/lib/calendar_floating_button.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +class CalendarFloatingButton extends StatelessWidget { + final DateTime date; + + const CalendarFloatingButton({super.key, required this.date}); + + @override + Widget build(BuildContext context) { + return FloatingActionButton( + onPressed: () async { + var router = GoRouter.of(context); + var datePicked = await showDatePicker( + locale: const Locale('de'), + context: context, + initialDate: date, + currentDate: DateTime.now(), + firstDate: DateTime.now().subtract(const Duration(days: 365 * 10)), + lastDate: DateTime.now(), + ); + + router.goNamed('perDay', extra: datePicked); + }, + child: const Icon(Icons.today)); + } +} diff --git a/lib/food_entry_bloc.dart b/lib/food_entry_bloc.dart index 39392d3..f7eb54f 100644 --- a/lib/food_entry_bloc.dart +++ b/lib/food_entry_bloc.dart @@ -9,6 +9,7 @@ class FoodEntryBloc extends Bloc { on(addFoodEntry); on(deleteFood); on(updateEntries); + on(persistDailyLimit); } void addFoodEntry(FoodEntryEvent event, Emitter emit) async { @@ -31,9 +32,19 @@ class FoodEntryBloc extends Bloc { void updateEntries( PageChangedEvent event, Emitter emit) async { var entries = await storage.getEntriesForDate(event.changedToDate); - var newState = FoodEntryState(foodEntries: entries); + var limit = await storage.readLimit(); + var newState = FoodEntryState(foodEntries: entries, kcalLimit: limit); emit(newState); } + + void persistDailyLimit( + DailyKcalLimitUpdated event, Emitter emit) async { + await storage.updateLimit(event.kcal); + emit(FoodEntryState(foodEntries: state.foodEntries, kcalLimit: event.kcal)); + } + + void getDailyLimit( + DailyKcalLimitUpdated event, Emitter emit) async {} } class FoodEvent {} @@ -58,18 +69,24 @@ class PageChangedEvent extends FoodEvent { PageChangedEvent({required this.changedToDate}); } +class DailyKcalLimitUpdated extends FoodEvent { + final double kcal; + DailyKcalLimitUpdated({required this.kcal}); +} + class FoodEntryState { final List foodEntries; + final double kcalLimit; - FoodEntryState({required this.foodEntries}); + FoodEntryState({required this.foodEntries, required this.kcalLimit}); factory FoodEntryState.init() { - return FoodEntryState(foodEntries: []); + return FoodEntryState(foodEntries: [], kcalLimit: 0); } static from(FoodEntryState state) { List newList = List.from(state.foodEntries); - return FoodEntryState(foodEntries: newList); + return FoodEntryState(foodEntries: newList, kcalLimit: state.kcalLimit); } void addEntry(FoodEntry entry) { diff --git a/lib/main.dart b/lib/main.dart index 13c0649..8c082c1 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -4,6 +4,7 @@ import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:go_router/go_router.dart'; import 'package:kalodings/food_entry_bloc.dart'; import 'package:kalodings/perdate_widget.dart'; +import 'package:kalodings/settings.dart'; import 'package:kalodings/storage/storage.dart'; void main() async { @@ -65,4 +66,11 @@ final router = GoRouter(routes: [ return PerDateWidget(date); }), + GoRoute( + path: '/settings', + name: 'settings', + builder: (context, state) { + return const SettingsWidget(); + }, + ) ]); diff --git a/lib/perdate_widget.dart b/lib/perdate_widget.dart index 9b43454..2079974 100644 --- a/lib/perdate_widget.dart +++ b/lib/perdate_widget.dart @@ -1,7 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:go_router/go_router.dart'; +import 'package:intl/intl.dart'; import 'package:kalodings/app_drawer.dart'; +import 'package:kalodings/calendar_floating_button.dart'; import 'package:kalodings/enter_food_widget.dart'; import 'package:kalodings/food_entry_bloc.dart'; import 'package:kalodings/food_entry_widget.dart'; @@ -13,58 +14,43 @@ class PerDateWidget extends StatelessWidget { @override Widget build(BuildContext context) { + var formattedDate = DateFormat.yMMMMd('de').format(date); return Scaffold( - appBar: AppBar( - title: Text(date.toString()), - ), - drawer: const AppDrawer(), - body: BlocBuilder( - 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 EnterFoodWidget( - onAdd: (context, entry) { - context - .read() - .add(FoodEntryEvent(entry: entry, date: date)); + appBar: AppBar( + title: Text(formattedDate), + ), + drawer: const AppDrawer(), + body: BlocBuilder( + builder: (context, state) { + return ListView.builder( + itemCount: state.foodEntries.length + 2, + itemBuilder: (BuildContext itemBuilderContext, int index) { + if (index == state.foodEntries.length) { + return SumWidget(limit: state.kcalLimit); + } + if (index == state.foodEntries.length + 1) { + return EnterFoodWidget( + onAdd: (context, entry) { + context + .read() + .add(FoodEntryEvent(entry: entry, date: date)); + }, + ); + } + + return FoodEntryWidget( + entry: state.foodEntries[index], + onDelete: (callbackContext) { + callbackContext.read().add( + FoodDeletionEvent( + entryID: state.foodEntries[index].id, date: date), + ); }, ); - } - - return FoodEntryWidget( - entry: state.foodEntries[index], - onDelete: (callbackContext) { - callbackContext.read().add( - FoodDeletionEvent( - entryID: state.foodEntries[index].id, date: date), - ); - }, - ); - }, - ); - }, - ), - floatingActionButton: FloatingActionButton( - onPressed: () async { - var router = GoRouter.of(context); - var datePicked = await showDatePicker( - locale: const Locale('de'), - context: context, - initialDate: date, - currentDate: DateTime.now(), - firstDate: - DateTime.now().subtract(const Duration(days: 365 * 10)), - lastDate: DateTime.now(), + }, ); - - router.goNamed('perDay', extra: datePicked); }, - child: const Icon(Icons.today)), - ); + ), + floatingActionButton: CalendarFloatingButton(date: date)); } } diff --git a/lib/settings.dart b/lib/settings.dart new file mode 100644 index 0000000..15c9a62 --- /dev/null +++ b/lib/settings.dart @@ -0,0 +1,66 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:go_router/go_router.dart'; +import 'package:kalodings/app_drawer.dart'; +import 'package:kalodings/calendar_floating_button.dart'; +import 'package:kalodings/food_entry_bloc.dart'; +import 'package:settings_ui/settings_ui.dart'; + +class SettingsWidget extends StatefulWidget { + const SettingsWidget({super.key}); + + @override + State createState() => _SettingsWidgetState(); +} + +class _SettingsWidgetState extends State { + var kcalPerDayCtrl = TextEditingController(); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: BlocBuilder( + builder: (context, state) { + return SettingsList(sections: [ + SettingsSection( + title: const Text('Deine persönlichen Einstellungen'), + tiles: [ + SettingsTile.navigation( + leading: const Icon(Icons.food_bank), + title: const Text('Kalorienlimit pro Tag'), + value: Text(state.kcalLimit.toString()), + onPressed: (context) async { + await showDialog( + builder: (ctx) { + return AlertDialog( + title: const Text("Kalorienlimit pro Tag"), + content: TextField(controller: kcalPerDayCtrl), + actions: [ + TextButton( + onPressed: () { + double setting; + try { + setting = + double.parse(kcalPerDayCtrl.text); + } catch (e) { + setting = 2000.0; + } + context.read().add( + DailyKcalLimitUpdated(kcal: setting)); + ctx.pop(); + }, + child: const Text('Ok')) + ], + ); + }, + context: context); + }), + ], + ), + ]); + }), + drawer: const AppDrawer(), + appBar: AppBar(title: const Text('Einstellungen')), + floatingActionButton: CalendarFloatingButton(date: DateTime.now())); + } +} diff --git a/lib/storage/storage.dart b/lib/storage/storage.dart index a601e2b..9bf659a 100644 --- a/lib/storage/storage.dart +++ b/lib/storage/storage.dart @@ -56,4 +56,37 @@ class FoodStorage { await file.writeAsString(fullString); } + + Future updateLimit(double limit) async { + var filePath = '$path/limit'; + var file = File(filePath); + + var exists = await file.exists(); + if (!exists) { + await file.create(); + } + + await file.writeAsString(limit.toString()); + } + + Future readLimit() async { + var filePath = '$path/limit'; + var file = File(filePath); + var exists = await file.exists(); + + if (!exists) { + return 2000; + } + + var line = await file.readAsLines(); + + double limit; + try { + limit = double.parse(line[0]); + } catch (e) { + limit = 2000; + } + + return limit; + } } diff --git a/lib/sum_widget.dart b/lib/sum_widget.dart index 7b82456..1b329ae 100644 --- a/lib/sum_widget.dart +++ b/lib/sum_widget.dart @@ -4,7 +4,9 @@ import 'package:kalodings/food_entry_bloc.dart'; import 'package:kalodings/row_with_spacers_widget.dart'; class SumWidget extends StatelessWidget { - const SumWidget({super.key}); + final double limit; + + const SumWidget({required this.limit, super.key}); @override Widget build(BuildContext context) { @@ -19,7 +21,7 @@ class SumWidget extends StatelessWidget { null, null, const Text("kcal heute:"), - Text(sum.toString()), + Text('${sum.toString()}/$limit'), null, ); }, diff --git a/pubspec.lock b/pubspec.lock index bbfe2a1..7bd45e2 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -309,6 +309,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.2.1" + settings_ui: + dependency: "direct main" + description: + name: settings_ui + sha256: d9838037cb554b24b4218b2d07666fbada3478882edefae375ee892b6c820ef3 + url: "https://pub.dev" + source: hosted + version: "2.0.2" shared_preferences: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 6563a44..4f4465b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,6 +17,7 @@ dependencies: intl: any path_provider: ^2.1.3 quiver: ^3.2.1 + settings_ui: ^2.0.2 uuid: ^4.4.0 dev_dependencies: