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

View File

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

View File

@ -3,20 +3,25 @@ import 'package:kalodings/storage/storage.dart';
import 'package:uuid/uuid.dart';
class FoodEntryBloc extends Bloc<FoodEvent, FoodEntryState> {
final FoodEntryState initialState;
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<FoodDeletionEvent>(deleteFood);
on<PageChangedEvent>(updateEntries);
on<DailyKcalLimitUpdated>(persistDailyLimit);
}
void addFoodEntry(FoodEntryEvent event, Emitter<FoodEntryState> emit) async {
FoodEntryState newState = FoodEntryState.from(state);
newState.addEntry(event.entry);
await storage.writeEntriesForDate(event.date, newState.foodEntries);
await storage.writeEntriesForDate(forDate, newState.foodEntries);
emit(newState);
}
@ -24,7 +29,7 @@ class FoodEntryBloc extends Bloc<FoodEvent, FoodEntryState> {
void deleteFood(FoodDeletionEvent event, Emitter<FoodEntryState> emit) async {
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));
}
@ -32,35 +37,23 @@ class FoodEntryBloc extends Bloc<FoodEvent, FoodEntryState> {
void updateEntries(
PageChangedEvent event, Emitter<FoodEntryState> emit) async {
var entries = await storage.getEntriesForDate(event.changedToDate);
var limit = await storage.readLimit();
var newState = FoodEntryState(foodEntries: entries, kcalLimit: limit);
var newState = FoodEntryState(foodEntries: entries);
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 FoodEntryEvent extends FoodEvent {
final FoodEntry entry;
final DateTime date;
FoodEntryEvent({required this.entry, required this.date});
FoodEntryEvent({required this.entry});
}
class FoodDeletionEvent extends FoodEvent {
final String entryID;
final DateTime date;
FoodDeletionEvent({required this.entryID, required this.date});
FoodDeletionEvent({required this.entryID});
}
class PageChangedEvent extends FoodEvent {
@ -69,24 +62,18 @@ class PageChangedEvent extends FoodEvent {
PageChangedEvent({required this.changedToDate});
}
class DailyKcalLimitUpdated extends FoodEvent {
final double kcal;
DailyKcalLimitUpdated({required this.kcal});
}
class FoodEntryState {
final List<FoodEntry> foodEntries;
final double kcalLimit;
FoodEntryState({required this.foodEntries, required this.kcalLimit});
FoodEntryState({required this.foodEntries});
factory FoodEntryState.init() {
return FoodEntryState(foodEntries: [], kcalLimit: 0);
return FoodEntryState(foodEntries: []);
}
static from(FoodEntryState state) {
List<FoodEntry> newList = List.from(state.foodEntries);
return FoodEntryState(foodEntries: newList, kcalLimit: state.kcalLimit);
return FoodEntryState(foodEntries: newList);
}
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_localizations/flutter_localizations.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/settings.dart';
import 'package:kalodings/storage/storage.dart';
@ -10,20 +10,22 @@ import 'package:kalodings/storage/storage.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
var storage = await FoodStorage.create();
var kcalLimit = await storage.readLimit();
runApp(MainApp(storage: storage));
runApp(MainApp(storage: storage, kcalLimit: kcalLimit));
}
class MainApp extends StatelessWidget {
final FoodStorage storage;
const MainApp({required this.storage, super.key});
final double kcalLimit;
const MainApp({required this.storage, required this.kcalLimit, super.key});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (BuildContext context) {
return FoodEntryBloc(FoodEntryState.init(), storage: storage);
},
create: (context) => SettingsDataBloc(SettingsState(kcalLimit: kcalLimit),
storage: storage),
child: MaterialApp.router(
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
@ -40,16 +42,7 @@ class MainApp extends StatelessWidget {
}
}
final router = GoRouter(routes: [
GoRoute(
path: '/',
name: 'perDayToday',
builder: (context, state) {
context
.read<FoodEntryBloc>()
.add(PageChangedEvent(changedToDate: DateTime.now()));
return PerDateWidget(DateTime.now());
}),
final router = GoRouter(initialLocation: '/day', routes: [
GoRoute(
path: '/day',
name: 'perDay',
@ -60,11 +53,8 @@ final router = GoRouter(routes: [
} else {
date = state.extra as DateTime;
}
context
.read<FoodEntryBloc>()
.add(PageChangedEvent(changedToDate: date));
return PerDateWidget(date);
return PerDateWidget(date: date);
}),
GoRoute(
path: '/settings',

View File

@ -6,59 +6,95 @@ 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';
import 'package:kalodings/storage/storage.dart';
import 'package:kalodings/sum_widget.dart';
class PerDateWidget extends StatelessWidget {
class PerDateWidget extends StatefulWidget {
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
Widget build(BuildContext context) {
var formattedDate = DateFormat.yMMMMd('de').format(date);
var formattedDate = DateFormat.yMMMMd('de').format(widget.date);
return Scaffold(
appBar: AppBar(
title: Text(formattedDate),
),
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 Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: SumWidget(limit: state.kcalLimit),
);
}
if (index == state.foodEntries.length + 1) {
return Column(
children: [
EnterFoodWidget(
onAdd: (context, entry) {
context
.read<FoodEntryBloc>()
.add(FoodEntryEvent(entry: entry, date: date));
},
),
const SizedBox(height: 75),
],
);
}
return FoodEntryWidget(
entry: state.foodEntries[index],
onDelete: (callbackContext) {
callbackContext.read<FoodEntryBloc>().add(
FoodDeletionEvent(
entryID: state.foodEntries[index].id, date: date),
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) {
return ListView.builder(
itemCount: state.foodEntries.length + 2,
itemBuilder: (BuildContext itemBuilderContext, int index) {
if (index == state.foodEntries.length) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: SumWidget(foodEntries: state.foodEntries),
);
},
);
},
);
}
if (index == state.foodEntries.length + 1) {
return Column(
children: [
EnterFoodWidget(
onAdd: (context, entry) {
context
.read<FoodEntryBloc>()
.add(FoodEntryEvent(entry: entry));
},
),
const SizedBox(height: 75),
],
);
}
return FoodEntryWidget(
entry: state.foodEntries[index],
onDelete: (callbackContext) {
callbackContext
.read<FoodEntryBloc>()
.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:kalodings/app_drawer.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';
class SettingsWidget extends StatefulWidget {
@ -19,7 +19,7 @@ class _SettingsWidgetState extends State<SettingsWidget> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: BlocBuilder<FoodEntryBloc, FoodEntryState>(
body: BlocBuilder<SettingsDataBloc, SettingsState>(
builder: (context, state) {
return SettingsList(sections: [
SettingsSection(
@ -45,7 +45,7 @@ class _SettingsWidgetState extends State<SettingsWidget> {
} catch (e) {
setting = 2000.0;
}
context.read<FoodEntryBloc>().add(
context.read<SettingsDataBloc>().add(
DailyKcalLimitUpdated(kcal: setting));
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';
class FoodStorage {
static late FoodStorage _instance;
late String path;
FoodStorage._create();
@ -11,10 +12,13 @@ class FoodStorage {
var storage = FoodStorage._create();
var directory = await getApplicationCacheDirectory();
storage.path = directory.path;
_instance = storage;
return storage;
return _instance;
}
static FoodStorage getInstance() => _instance;
Future<List<FoodEntry>> getEntriesForDate(DateTime date) async {
List<FoodEntry> entries = [];
var filePath = '$path/${date.year}/${date.month}/${date.day}';

View File

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