Instead of a global FoodEntryBloc, every PerDateWidget has one

This commit is contained in:
Marco 2024-06-11 19:05:42 +02:00
parent d2ab82aaf5
commit be2ab69a3f
9 changed files with 162 additions and 102 deletions

View File

@ -20,7 +20,7 @@ class AppDrawer extends StatelessWidget {
leading: IconButton( leading: IconButton(
icon: const Icon(Icons.close), icon: const Icon(Icons.close),
onPressed: () { onPressed: () {
Navigator.pop(context); context.pop();
}, },
), ),
title: const Text('Menü')), title: const Text('Menü')),
@ -29,7 +29,8 @@ class AppDrawer extends StatelessWidget {
title: const Text('Einstellungen'), title: const Text('Einstellungen'),
trailing: const Icon(Icons.settings), trailing: const Icon(Icons.settings),
onTap: () { onTap: () {
context.goNamed('settings'); context.pop();
context.pushNamed('settings');
}, },
), ),
], ],

View File

@ -20,7 +20,7 @@ class CalendarFloatingButton extends StatelessWidget {
lastDate: DateTime.now(), lastDate: DateTime.now(),
); );
router.goNamed('perDay', extra: datePicked); router.pushNamed('perDay', extra: datePicked);
}, },
child: const Icon(Icons.today)); child: const Icon(Icons.today));
} }

View File

@ -3,20 +3,25 @@ import 'package:kalodings/storage/storage.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
class FoodEntryBloc extends Bloc<FoodEvent, FoodEntryState> { class FoodEntryBloc extends Bloc<FoodEvent, FoodEntryState> {
final FoodEntryState initialState;
final FoodStorage storage; final FoodStorage storage;
final DateTime forDate;
FoodEntryBloc(super.initialState, {required this.storage}) { FoodEntryBloc(
{required this.initialState,
required this.forDate,
required this.storage})
: super(initialState) {
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 {
FoodEntryState newState = FoodEntryState.from(state); FoodEntryState newState = FoodEntryState.from(state);
newState.addEntry(event.entry); newState.addEntry(event.entry);
await storage.writeEntriesForDate(event.date, newState.foodEntries); await storage.writeEntriesForDate(forDate, newState.foodEntries);
emit(newState); emit(newState);
} }
@ -24,7 +29,7 @@ class FoodEntryBloc extends Bloc<FoodEvent, FoodEntryState> {
void deleteFood(FoodDeletionEvent event, Emitter<FoodEntryState> emit) async { void deleteFood(FoodDeletionEvent event, Emitter<FoodEntryState> emit) async {
state.foodEntries.removeWhere((entry) => entry.id == event.entryID); state.foodEntries.removeWhere((entry) => entry.id == event.entryID);
await storage.writeEntriesForDate(event.date, state.foodEntries); await storage.writeEntriesForDate(forDate, state.foodEntries);
emit(FoodEntryState.from(state)); emit(FoodEntryState.from(state));
} }
@ -32,35 +37,23 @@ 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 limit = await storage.readLimit(); var newState = FoodEntryState(foodEntries: entries);
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 {}
class FoodEntryEvent extends FoodEvent { class FoodEntryEvent extends FoodEvent {
final FoodEntry entry; final FoodEntry entry;
final DateTime date;
FoodEntryEvent({required this.entry, required this.date}); FoodEntryEvent({required this.entry});
} }
class FoodDeletionEvent extends FoodEvent { class FoodDeletionEvent extends FoodEvent {
final String entryID; final String entryID;
final DateTime date;
FoodDeletionEvent({required this.entryID, required this.date}); FoodDeletionEvent({required this.entryID});
} }
class PageChangedEvent extends FoodEvent { class PageChangedEvent extends FoodEvent {
@ -69,24 +62,18 @@ 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, required this.kcalLimit}); FoodEntryState({required this.foodEntries});
factory FoodEntryState.init() { factory FoodEntryState.init() {
return FoodEntryState(foodEntries: [], kcalLimit: 0); return FoodEntryState(foodEntries: []);
} }
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, kcalLimit: state.kcalLimit); return FoodEntryState(foodEntries: newList);
} }
void addEntry(FoodEntry entry) { void addEntry(FoodEntry entry) {

View File

@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_localizations/flutter_localizations.dart'; 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/settings_bloc.dart';
import 'package:kalodings/perdate_widget.dart'; import 'package:kalodings/perdate_widget.dart';
import 'package:kalodings/settings.dart'; import 'package:kalodings/settings.dart';
import 'package:kalodings/storage/storage.dart'; import 'package:kalodings/storage/storage.dart';
@ -10,20 +10,22 @@ import 'package:kalodings/storage/storage.dart';
void main() async { void main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
var storage = await FoodStorage.create(); var storage = await FoodStorage.create();
var kcalLimit = await storage.readLimit();
runApp(MainApp(storage: storage)); runApp(MainApp(storage: storage, kcalLimit: kcalLimit));
} }
class MainApp extends StatelessWidget { class MainApp extends StatelessWidget {
final FoodStorage storage; final FoodStorage storage;
const MainApp({required this.storage, super.key}); final double kcalLimit;
const MainApp({required this.storage, required this.kcalLimit, super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (BuildContext context) { create: (context) => SettingsDataBloc(SettingsState(kcalLimit: kcalLimit),
return FoodEntryBloc(FoodEntryState.init(), storage: storage); storage: storage),
},
child: MaterialApp.router( child: MaterialApp.router(
localizationsDelegates: const [ localizationsDelegates: const [
GlobalMaterialLocalizations.delegate, GlobalMaterialLocalizations.delegate,
@ -40,16 +42,7 @@ class MainApp extends StatelessWidget {
} }
} }
final router = GoRouter(routes: [ final router = GoRouter(initialLocation: '/day', routes: [
GoRoute(
path: '/',
name: 'perDayToday',
builder: (context, state) {
context
.read<FoodEntryBloc>()
.add(PageChangedEvent(changedToDate: DateTime.now()));
return PerDateWidget(DateTime.now());
}),
GoRoute( GoRoute(
path: '/day', path: '/day',
name: 'perDay', name: 'perDay',
@ -60,11 +53,8 @@ final router = GoRouter(routes: [
} else { } else {
date = state.extra as DateTime; date = state.extra as DateTime;
} }
context
.read<FoodEntryBloc>()
.add(PageChangedEvent(changedToDate: date));
return PerDateWidget(date); return PerDateWidget(date: date);
}), }),
GoRoute( GoRoute(
path: '/settings', path: '/settings',

View File

@ -6,21 +6,53 @@ 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';
import 'package:kalodings/storage/storage.dart';
import 'package:kalodings/sum_widget.dart'; import 'package:kalodings/sum_widget.dart';
class PerDateWidget extends StatelessWidget { class PerDateWidget extends StatefulWidget {
final DateTime date; final DateTime date;
const PerDateWidget(this.date, {super.key}); const PerDateWidget({super.key, required this.date});
@override
State<PerDateWidget> createState() => _PerDateWidgetState();
}
class _PerDateWidgetState extends State<PerDateWidget> {
late FoodStorage storage;
late Future<List<FoodEntry>> entriesFuture;
late List<FoodEntry> entries;
@override
void initState() {
super.initState();
storage = FoodStorage.getInstance();
entriesFuture = storage.getEntriesForDate(widget.date);
entriesFuture.then((val) {
entries = val;
});
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var formattedDate = DateFormat.yMMMMd('de').format(date); var formattedDate = DateFormat.yMMMMd('de').format(widget.date);
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(formattedDate), title: Text(formattedDate),
), ),
drawer: const AppDrawer(), drawer: const AppDrawer(),
body: BlocBuilder<FoodEntryBloc, FoodEntryState>( body: FutureBuilder(
future: entriesFuture,
builder: (context, snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return const Center(child: CircularProgressIndicator());
} else {
return BlocProvider(
create: (context) => FoodEntryBloc(
initialState: FoodEntryState(foodEntries: entries),
storage: storage,
forDate: widget.date),
child: 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,
@ -28,7 +60,7 @@ class PerDateWidget extends StatelessWidget {
if (index == state.foodEntries.length) { if (index == state.foodEntries.length) {
return Padding( return Padding(
padding: const EdgeInsets.symmetric(vertical: 10), padding: const EdgeInsets.symmetric(vertical: 10),
child: SumWidget(limit: state.kcalLimit), child: SumWidget(foodEntries: state.foodEntries),
); );
} }
if (index == state.foodEntries.length + 1) { if (index == state.foodEntries.length + 1) {
@ -38,7 +70,7 @@ class PerDateWidget extends StatelessWidget {
onAdd: (context, entry) { onAdd: (context, entry) {
context context
.read<FoodEntryBloc>() .read<FoodEntryBloc>()
.add(FoodEntryEvent(entry: entry, date: date)); .add(FoodEntryEvent(entry: entry));
}, },
), ),
const SizedBox(height: 75), const SizedBox(height: 75),
@ -49,16 +81,20 @@ class PerDateWidget extends StatelessWidget {
return FoodEntryWidget( return FoodEntryWidget(
entry: state.foodEntries[index], entry: state.foodEntries[index],
onDelete: (callbackContext) { onDelete: (callbackContext) {
callbackContext.read<FoodEntryBloc>().add( callbackContext
FoodDeletionEvent( .read<FoodEntryBloc>()
entryID: state.foodEntries[index].id, date: date), .add(FoodDeletionEvent(
); entryID: state.foodEntries[index].id,
));
}, },
); );
}, },
); );
}),
);
}
}, },
), ),
floatingActionButton: CalendarFloatingButton(date: date)); floatingActionButton: CalendarFloatingButton(date: widget.date));
} }
} }

View File

@ -3,7 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:kalodings/app_drawer.dart'; import 'package:kalodings/app_drawer.dart';
import 'package:kalodings/calendar_floating_button.dart'; import 'package:kalodings/calendar_floating_button.dart';
import 'package:kalodings/food_entry_bloc.dart'; import 'package:kalodings/settings_bloc.dart';
import 'package:settings_ui/settings_ui.dart'; import 'package:settings_ui/settings_ui.dart';
class SettingsWidget extends StatefulWidget { class SettingsWidget extends StatefulWidget {
@ -19,7 +19,7 @@ class _SettingsWidgetState extends State<SettingsWidget> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
body: BlocBuilder<FoodEntryBloc, FoodEntryState>( body: BlocBuilder<SettingsDataBloc, SettingsState>(
builder: (context, state) { builder: (context, state) {
return SettingsList(sections: [ return SettingsList(sections: [
SettingsSection( SettingsSection(
@ -45,7 +45,7 @@ class _SettingsWidgetState extends State<SettingsWidget> {
} catch (e) { } catch (e) {
setting = 2000.0; setting = 2000.0;
} }
context.read<FoodEntryBloc>().add( context.read<SettingsDataBloc>().add(
DailyKcalLimitUpdated(kcal: setting)); DailyKcalLimitUpdated(kcal: setting));
ctx.pop(); ctx.pop();
}, },

37
lib/settings_bloc.dart Normal file
View File

@ -0,0 +1,37 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:kalodings/storage/storage.dart';
class SettingsDataBloc extends Bloc<SettingsEvent, SettingsState> {
final FoodStorage storage;
SettingsDataBloc(super.initialState, {required this.storage}) {
on<DailyKcalLimitUpdated>(persistDailyLimit);
}
void persistDailyLimit(
DailyKcalLimitUpdated event, Emitter<SettingsState> emit) async {
await storage.updateLimit(event.kcal);
emit(SettingsState(kcalLimit: event.kcal));
}
}
class SettingsEvent {}
class DailyKcalLimitUpdated extends SettingsEvent {
final double kcal;
DailyKcalLimitUpdated({required this.kcal});
}
class SettingsState {
final double kcalLimit;
SettingsState({required this.kcalLimit});
factory SettingsState.init() {
return SettingsState(kcalLimit: 2000);
}
static from(SettingsState state) {
return SettingsState(kcalLimit: state.kcalLimit);
}
}

View File

@ -4,6 +4,7 @@ import 'package:kalodings/food_entry_bloc.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
class FoodStorage { class FoodStorage {
static late FoodStorage _instance;
late String path; late String path;
FoodStorage._create(); FoodStorage._create();
@ -11,10 +12,13 @@ class FoodStorage {
var storage = FoodStorage._create(); var storage = FoodStorage._create();
var directory = await getApplicationCacheDirectory(); var directory = await getApplicationCacheDirectory();
storage.path = directory.path; storage.path = directory.path;
_instance = storage;
return storage; return _instance;
} }
static FoodStorage getInstance() => _instance;
Future<List<FoodEntry>> getEntriesForDate(DateTime date) async { Future<List<FoodEntry>> getEntriesForDate(DateTime date) async {
List<FoodEntry> entries = []; List<FoodEntry> entries = [];
var filePath = '$path/${date.year}/${date.month}/${date.day}'; var filePath = '$path/${date.year}/${date.month}/${date.day}';

View File

@ -1,19 +1,24 @@
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:kalodings/food_entry_bloc.dart'; import 'package:kalodings/food_entry_bloc.dart';
import 'package:kalodings/settings_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 StatefulWidget {
final double limit; final List<FoodEntry> foodEntries;
const SumWidget({required this.foodEntries, super.key});
const SumWidget({required this.limit, super.key});
@override
State<SumWidget> createState() => _SumWidgetState();
}
class _SumWidgetState extends State<SumWidget> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocBuilder<FoodEntryBloc, FoodEntryState>( return BlocBuilder<SettingsDataBloc, SettingsState>(
builder: (context, state) { builder: (context, state) {
var sum = 0.0; var sum = 0.0;
for (var entry in state.foodEntries) { for (var entry in widget.foodEntries) {
sum += entry.kcalPerMass / 100 * entry.mass; sum += entry.kcalPerMass / 100 * entry.mass;
} }
@ -21,7 +26,7 @@ class SumWidget extends StatelessWidget {
null, null,
null, null,
const Text("kcal heute:"), const Text("kcal heute:"),
Text('${sum.toString()}/$limit'), Text('${sum.toString()}/${state.kcalLimit}'),
null, null,
); );
}, },