Change behavior of global state

Until now, FoodEntryBloc (which is holding the global state  for every
day) would cause a change in every widget in the tree. For example, when
an entry for one day gets added, all other entries in opened days would
also be rebuilt.

Now, the GlobalState will be emitted with an additional date, which
signals, which date caused the state change.
With this information, I selectively only build the EntryLists that
needs to be rebuilt.

Additionally, the calendar FAB will push a new route instead of
navigating to a new day by utilizing the pageController.
This commit is contained in:
Marco 2025-01-12 17:16:55 +01:00
parent 7126b1b593
commit 7728ec3b66
3 changed files with 35 additions and 13 deletions

View File

@ -27,7 +27,8 @@ class FoodEntryBloc extends Bloc<FoodEvent, GlobalEntryState> {
var newList = await storage.getEntriesForDate(event.forDate);
state.foodEntries.addAll({event.forDate: newList});
emit(GlobalEntryState(foodEntries: state.foodEntries));
emit(GlobalEntryState(
foodEntries: state.foodEntries, stateChangedForDate: event.forDate));
}
void handleFoodEntryEvent(
@ -43,7 +44,8 @@ class FoodEntryBloc extends Bloc<FoodEvent, GlobalEntryState> {
var newFoodEntries = state.foodEntries;
newFoodEntries.addAll({event.forDate: entriesForDate});
emit(GlobalEntryState(foodEntries: newFoodEntries));
emit(GlobalEntryState(
foodEntries: newFoodEntries, stateChangedForDate: event.forDate));
}
void handleFoodChangedEvent(
@ -64,7 +66,8 @@ class FoodEntryBloc extends Bloc<FoodEvent, GlobalEntryState> {
var newFoodEntries = state.foodEntries;
newFoodEntries.addAll({event.forDate: entriesForDate});
emit(GlobalEntryState(foodEntries: newFoodEntries));
emit(GlobalEntryState(
foodEntries: newFoodEntries, stateChangedForDate: event.forDate));
}
void handleDeleteFoodEvent(
@ -79,7 +82,8 @@ class FoodEntryBloc extends Bloc<FoodEvent, GlobalEntryState> {
var newFoodEntries = state.foodEntries;
newFoodEntries.addAll({event.forDate: entriesForDate});
emit(GlobalEntryState(foodEntries: newFoodEntries));
emit(GlobalEntryState(
foodEntries: newFoodEntries, stateChangedForDate: event.forDate));
}
void handleBarcodeScannedEvent(
@ -91,6 +95,7 @@ class FoodEntryBloc extends Bloc<FoodEvent, GlobalEntryState> {
if (e.code == BarcodeScanner.cameraAccessDenied) {
emit(GlobalEntryState(
foodEntries: state.foodEntries,
stateChangedForDate: event.forDate,
appError:
GlobalAppError(GlobalAppErrorType.errCameraPermissionDenied)));
}
@ -107,6 +112,7 @@ class FoodEntryBloc extends Bloc<FoodEvent, GlobalEntryState> {
if (scanResult.type == ResultType.Error) {
emit(GlobalEntryState(
foodEntries: state.foodEntries,
stateChangedForDate: event.forDate,
appError: GlobalAppError(GlobalAppErrorType.errGeneralError)));
return;
}
@ -122,7 +128,10 @@ class FoodEntryBloc extends Bloc<FoodEvent, GlobalEntryState> {
entriesForDate.add(newEntryWaiting);
state.foodEntries.addAll({event.forDate: entriesForDate});
emit(GlobalEntryState(foodEntries: state.foodEntries));
emit(GlobalEntryState(
foodEntries: state.foodEntries,
stateChangedForDate: event.forDate,
));
await responseFuture.then((response) async {
var index = entriesForDate
@ -140,6 +149,7 @@ class FoodEntryBloc extends Bloc<FoodEvent, GlobalEntryState> {
emit(GlobalEntryState(
foodEntries: newFoodEntries,
stateChangedForDate: event.forDate,
appError: GlobalAppError(GlobalAppErrorType.errbarcodeNotFound)));
return;
}
@ -151,6 +161,7 @@ class FoodEntryBloc extends Bloc<FoodEvent, GlobalEntryState> {
emit(GlobalEntryState(
foodEntries: newFoodEntries,
stateChangedForDate: event.forDate,
appError:
GlobalAppError(GlobalAppErrorType.errServerNotReachable)));
return;
@ -173,7 +184,8 @@ class FoodEntryBloc extends Bloc<FoodEvent, GlobalEntryState> {
var newFoodEntries = state.foodEntries;
newFoodEntries.addAll({event.forDate: entriesForDate});
emit(GlobalEntryState(foodEntries: newFoodEntries));
emit(GlobalEntryState(
foodEntries: newFoodEntries, stateChangedForDate: event.forDate));
});
}
@ -194,7 +206,8 @@ class FoodEntryBloc extends Bloc<FoodEvent, GlobalEntryState> {
selectedEntry.isSelected = !oldStateOfTappedEntry;
emit(GlobalEntryState(foodEntries: state.foodEntries));
emit(GlobalEntryState(
foodEntries: state.foodEntries, stateChangedForDate: event.forDate));
}
}
@ -240,12 +253,17 @@ class PermissionException extends FoodEvent {
PermissionException({required super.forDate});
}
/// This is the state for one date/page
class PageEntryState {}
class GlobalEntryState {
final Map<DateTime, List<FoodEntryState>> foodEntries;
final GlobalAppError? appError;
GlobalEntryState({required this.foodEntries, this.appError});
//we use this to only redraw pages whose entries changed
final DateTime? stateChangedForDate;
GlobalEntryState(
{required this.foodEntries, this.stateChangedForDate, this.appError});
factory GlobalEntryState.init() {
return GlobalEntryState(foodEntries: {});

View File

@ -92,10 +92,9 @@ class PerDatePageViewController extends StatelessWidget {
onDateSelected: (dateSelected) {
if (dateSelected == null) return;
var dateDiff = dateSelected.difference(initialDate).inDays;
log("dateDiff = $dateDiff");
pageController.jumpToPage(initialOffset - dateDiff);
Navigator.of(context).push(MaterialPageRoute(
builder: (context) =>
PerDatePageViewController(initialDate: dateSelected)));
},
),
],

View File

@ -33,6 +33,11 @@ class _PerDateWidgetState extends State<PerDateWidget>
showNewSnackbarWith(context, pageState.appError!);
}
},
buildWhen: (previous, current) {
if (current.stateChangedForDate == null) return true;
if (current.stateChangedForDate == widget.date) return true;
return false;
},
builder: (context, pageState) {
return FoodEntryList(
entries: pageState.foodEntries[widget.date] ?? [],