Introduce settings for daily kcal limit
This commit is contained in:
parent
7b0947f93f
commit
7a6062fd95
@ -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');
|
||||||
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
27
lib/calendar_floating_button.dart
Normal file
27
lib/calendar_floating_button.dart
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
@ -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) {
|
||||||
|
@ -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();
|
||||||
|
},
|
||||||
|
)
|
||||||
]);
|
]);
|
||||||
|
@ -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
66
lib/settings.dart
Normal 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()));
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -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:
|
||||||
|
@ -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:
|
||||||
|
Loading…
Reference in New Issue
Block a user