Introduce settings for daily kcal limit

This commit is contained in:
Marco 2024-06-10 03:06:56 +02:00
parent 7b0947f93f
commit 7a6062fd95
10 changed files with 207 additions and 56 deletions

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
class AppDrawer extends StatelessWidget { class AppDrawer extends StatelessWidget {
const AppDrawer({ const AppDrawer({
@ -27,7 +28,9 @@ class AppDrawer extends StatelessWidget {
ListTile( ListTile(
title: const Text('Einstellungen'), title: const Text('Einstellungen'),
trailing: const Icon(Icons.settings), trailing: const Icon(Icons.settings),
onTap: () {}, onTap: () {
context.goNamed('settings');
},
), ),
], ],
), ),

View File

@ -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));
}
}

View File

@ -9,6 +9,7 @@ class FoodEntryBloc extends Bloc<FoodEvent, FoodEntryState> {
on<FoodEntryEvent>(addFoodEntry); on<FoodEntryEvent>(addFoodEntry);
on<FoodDeletionEvent>(deleteFood); on<FoodDeletionEvent>(deleteFood);
on<PageChangedEvent>(updateEntries); on<PageChangedEvent>(updateEntries);
on<DailyKcalLimitUpdated>(persistDailyLimit);
} }
void addFoodEntry(FoodEntryEvent event, Emitter<FoodEntryState> emit) async { void addFoodEntry(FoodEntryEvent event, Emitter<FoodEntryState> emit) async {
@ -31,9 +32,19 @@ class FoodEntryBloc extends Bloc<FoodEvent, FoodEntryState> {
void updateEntries( void updateEntries(
PageChangedEvent event, Emitter<FoodEntryState> emit) async { PageChangedEvent event, Emitter<FoodEntryState> emit) async {
var entries = await storage.getEntriesForDate(event.changedToDate); 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); emit(newState);
} }
void persistDailyLimit(
DailyKcalLimitUpdated event, Emitter<FoodEntryState> emit) async {
await storage.updateLimit(event.kcal);
emit(FoodEntryState(foodEntries: state.foodEntries, kcalLimit: event.kcal));
}
void getDailyLimit(
DailyKcalLimitUpdated event, Emitter<FoodEntryState> emit) async {}
} }
class FoodEvent {} class FoodEvent {}
@ -58,18 +69,24 @@ class PageChangedEvent extends FoodEvent {
PageChangedEvent({required this.changedToDate}); PageChangedEvent({required this.changedToDate});
} }
class DailyKcalLimitUpdated extends FoodEvent {
final double kcal;
DailyKcalLimitUpdated({required this.kcal});
}
class FoodEntryState { class FoodEntryState {
final List<FoodEntry> foodEntries; final List<FoodEntry> foodEntries;
final double kcalLimit;
FoodEntryState({required this.foodEntries}); FoodEntryState({required this.foodEntries, required this.kcalLimit});
factory FoodEntryState.init() { factory FoodEntryState.init() {
return FoodEntryState(foodEntries: []); return FoodEntryState(foodEntries: [], kcalLimit: 0);
} }
static from(FoodEntryState state) { static from(FoodEntryState state) {
List<FoodEntry> newList = List.from(state.foodEntries); List<FoodEntry> newList = List.from(state.foodEntries);
return FoodEntryState(foodEntries: newList); return FoodEntryState(foodEntries: newList, kcalLimit: state.kcalLimit);
} }
void addEntry(FoodEntry entry) { void addEntry(FoodEntry entry) {

View File

@ -4,6 +4,7 @@ import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:kalodings/food_entry_bloc.dart'; import 'package:kalodings/food_entry_bloc.dart';
import 'package:kalodings/perdate_widget.dart'; import 'package:kalodings/perdate_widget.dart';
import 'package:kalodings/settings.dart';
import 'package:kalodings/storage/storage.dart'; import 'package:kalodings/storage/storage.dart';
void main() async { void main() async {
@ -65,4 +66,11 @@ final router = GoRouter(routes: [
return PerDateWidget(date); return PerDateWidget(date);
}), }),
GoRoute(
path: '/settings',
name: 'settings',
builder: (context, state) {
return const SettingsWidget();
},
)
]); ]);

View File

@ -1,7 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.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/app_drawer.dart';
import 'package:kalodings/calendar_floating_button.dart';
import 'package:kalodings/enter_food_widget.dart'; import 'package:kalodings/enter_food_widget.dart';
import 'package:kalodings/food_entry_bloc.dart'; import 'package:kalodings/food_entry_bloc.dart';
import 'package:kalodings/food_entry_widget.dart'; import 'package:kalodings/food_entry_widget.dart';
@ -13,58 +14,43 @@ class PerDateWidget extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var formattedDate = DateFormat.yMMMMd('de').format(date);
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(date.toString()), title: Text(formattedDate),
), ),
drawer: const AppDrawer(), drawer: const AppDrawer(),
body: BlocBuilder<FoodEntryBloc, FoodEntryState>( body: BlocBuilder<FoodEntryBloc, FoodEntryState>(
builder: (context, state) { builder: (context, state) {
return ListView.builder( return ListView.builder(
itemCount: state.foodEntries.length + 2, itemCount: state.foodEntries.length + 2,
itemBuilder: (BuildContext itemBuilderContext, int index) { itemBuilder: (BuildContext itemBuilderContext, int index) {
if (index == state.foodEntries.length) { if (index == state.foodEntries.length) {
return const SumWidget(); return SumWidget(limit: state.kcalLimit);
} }
if (index == state.foodEntries.length + 1) { if (index == state.foodEntries.length + 1) {
return EnterFoodWidget( return EnterFoodWidget(
onAdd: (context, entry) { onAdd: (context, entry) {
context context
.read<FoodEntryBloc>() .read<FoodEntryBloc>()
.add(FoodEntryEvent(entry: entry, date: date)); .add(FoodEntryEvent(entry: entry, date: date));
},
);
}
return FoodEntryWidget(
entry: state.foodEntries[index],
onDelete: (callbackContext) {
callbackContext.read<FoodEntryBloc>().add(
FoodDeletionEvent(
entryID: state.foodEntries[index].id, date: date),
);
}, },
); );
} },
return FoodEntryWidget(
entry: state.foodEntries[index],
onDelete: (callbackContext) {
callbackContext.read<FoodEntryBloc>().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));
} }
} }

66
lib/settings.dart Normal file
View File

@ -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<SettingsWidget> createState() => _SettingsWidgetState();
}
class _SettingsWidgetState extends State<SettingsWidget> {
var kcalPerDayCtrl = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
body: BlocBuilder<FoodEntryBloc, FoodEntryState>(
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<FoodEntryBloc>().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()));
}
}

View File

@ -56,4 +56,37 @@ class FoodStorage {
await file.writeAsString(fullString); await file.writeAsString(fullString);
} }
Future<void> 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<double> 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;
}
} }

View File

@ -4,7 +4,9 @@ import 'package:kalodings/food_entry_bloc.dart';
import 'package:kalodings/row_with_spacers_widget.dart'; import 'package:kalodings/row_with_spacers_widget.dart';
class SumWidget extends StatelessWidget { class SumWidget extends StatelessWidget {
const SumWidget({super.key}); final double limit;
const SumWidget({required this.limit, super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -19,7 +21,7 @@ class SumWidget extends StatelessWidget {
null, null,
null, null,
const Text("kcal heute:"), const Text("kcal heute:"),
Text(sum.toString()), Text('${sum.toString()}/$limit'),
null, null,
); );
}, },

View File

@ -309,6 +309,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.2.1" 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: shared_preferences:
dependency: transitive dependency: transitive
description: description:

View File

@ -17,6 +17,7 @@ dependencies:
intl: any intl: any
path_provider: ^2.1.3 path_provider: ^2.1.3
quiver: ^3.2.1 quiver: ^3.2.1
settings_ui: ^2.0.2
uuid: ^4.4.0 uuid: ^4.4.0
dev_dependencies: dev_dependencies: