From f28626c49e16b4b6bab78889fea14c76b3ce9470 Mon Sep 17 00:00:00 2001 From: Marco Date: Wed, 25 Sep 2024 17:33:40 +0200 Subject: [PATCH] Move select logic for FoodEntry into BLoC --- lib/food_entry/enter_food_widget.dart | 2 +- lib/food_entry/food_entry_bloc.dart | 49 +++++++++++++++++++++++++-- lib/food_entry/food_entry_widget.dart | 34 +++++++------------ lib/food_scan/food_fact_lookup.dart | 1 + lib/perdate/entry_list.dart | 5 +++ lib/storage/storage.dart | 1 + 6 files changed, 67 insertions(+), 25 deletions(-) diff --git a/lib/food_entry/enter_food_widget.dart b/lib/food_entry/enter_food_widget.dart index 7501520..fd76acc 100644 --- a/lib/food_entry/enter_food_widget.dart +++ b/lib/food_entry/enter_food_widget.dart @@ -2,7 +2,6 @@ import 'package:calorimeter/storage/storage.dart'; import 'package:flutter/material.dart'; import 'package:calorimeter/food_entry/food_entry_bloc.dart'; import 'package:calorimeter/utils/row_with_spacers_widget.dart'; -import 'package:provider/provider.dart'; class EnterFoodWidget extends StatefulWidget { final Function(BuildContext context, FoodEntryState entry) onAdd; @@ -128,6 +127,7 @@ class _EnterFoodWidgetState extends State { mass: massAsNumber, kcalPer100: kcalPerMassAsNumber, waitingForNetwork: false, + isSelected: false, ); widget.onAdd(context, entry); diff --git a/lib/food_entry/food_entry_bloc.dart b/lib/food_entry/food_entry_bloc.dart index f0e847a..acf2071 100644 --- a/lib/food_entry/food_entry_bloc.dart +++ b/lib/food_entry/food_entry_bloc.dart @@ -18,6 +18,7 @@ class FoodEntryBloc extends Bloc { on(handleFoodChangedEvent); on(handleDeleteFoodEvent); on(handleBarcodeScannedEvent); + on(handleFoodEntryTapped); } void handleFoodEntryEvent( @@ -74,12 +75,23 @@ class FoodEntryBloc extends Bloc { List newList = List.from(state.foodEntries); var newEntryWaiting = FoodEntryState( - kcalPer100: 0, name: "", mass: 0, waitingForNetwork: true); + kcalPer100: 0, + name: "", + mass: 0, + waitingForNetwork: true, + isSelected: false, + ); newList.add(newEntryWaiting); emit(PageState(foodEntries: newList)); await responseFuture.then((response) { - newList.removeWhere((entryState) => entryState.id == newEntryWaiting.id); + var index = newList + .indexWhere((entryState) => entryState.id == newEntryWaiting.id); + + // element not found (was deleted previously) + if (index == -1) { + return; + } if (response.status == FoodFactResponseStatus.barcodeNotFound) { emit(PageState( @@ -100,11 +112,29 @@ class FoodEntryBloc extends Bloc { mass: response.food?.mass ?? 0, kcalPer100: response.food?.kcalPer100g ?? 0, waitingForNetwork: false, + isSelected: false, ); - newList.add(newEntryFinishedWaiting); + + newList.removeAt(index); + newList.insert(index, newEntryFinishedWaiting); emit(PageState(foodEntries: newList)); }); } + + void handleFoodEntryTapped( + FoodEntryTapped event, Emitter emit) async { + for (var entry in state.foodEntries) { + entry.isSelected = false; + } + + var selectedEntry = state.foodEntries.firstWhere((entry) { + return entry.id == event.entryID; + }); + + selectedEntry.selected = !selectedEntry.selected; + + emit(PageState(foodEntries: state.foodEntries)); + } } class FoodEvent {} @@ -133,6 +163,12 @@ class BarcodeScanned extends FoodEvent { BarcodeScanned({required this.scanResultFuture}); } +class FoodEntryTapped extends FoodEvent { + final String entryID; + + FoodEntryTapped({required this.entryID}); +} + /// This is the state for one date/page class PageState { final List foodEntries; @@ -159,12 +195,14 @@ class FoodEntryState { final int kcalPer100; final String id; final bool waitingForNetwork; + bool isSelected; factory FoodEntryState({ required name, required mass, required kcalPer100, required waitingForNetwork, + required isSelected, }) { return FoodEntryState.withID( id: const Uuid().v1(), @@ -172,6 +210,7 @@ class FoodEntryState { mass: mass, kcalPer100: kcalPer100, waitingForNetwork: waitingForNetwork, + isSelected: isSelected, ); } @@ -181,8 +220,12 @@ class FoodEntryState { required this.mass, required this.kcalPer100, required this.waitingForNetwork, + required this.isSelected, }); + set selected(bool selected) => isSelected = selected; + bool get selected => isSelected; + @override String toString() { //we use quotation marks around the name because the name might contain diff --git a/lib/food_entry/food_entry_widget.dart b/lib/food_entry/food_entry_widget.dart index 02f9df4..49bda5e 100644 --- a/lib/food_entry/food_entry_widget.dart +++ b/lib/food_entry/food_entry_widget.dart @@ -6,12 +6,14 @@ class FoodEntryWidget extends StatefulWidget { final FoodEntryState entry; final Function(BuildContext context, String id) onDelete; final Function(BuildContext context, FoodEntryState entry) onChange; + final Function(BuildContext context, FoodEntryState entry) onTap; const FoodEntryWidget({ super.key, required this.entry, required this.onDelete, required this.onChange, + required this.onTap, }); @override @@ -19,22 +21,15 @@ class FoodEntryWidget extends StatefulWidget { } class _FoodEntryWidgetState extends State { - late bool showCancelAndDelete; - @override void initState() { - showCancelAndDelete = false; super.initState(); } @override Widget build(BuildContext context) { return GestureDetector( - onTap: () { - setState(() { - showCancelAndDelete = !showCancelAndDelete; - }); - }, + onTap: () => widget.onTap(context, widget.entry), child: Stack( children: [ Positioned.fill( @@ -51,14 +46,14 @@ class _FoodEntryWidgetState extends State { : Text(widget.entry.mass.ceil().toString(), textAlign: TextAlign.end), Opacity( - opacity: showCancelAndDelete ? 0.0 : 1.0, + opacity: widget.entry.isSelected ? 0.0 : 1.0, child: widget.entry.waitingForNetwork ? Container() : Text(widget.entry.kcalPer100.ceil().toString(), textAlign: TextAlign.end), ), Opacity( - opacity: showCancelAndDelete ? 0.0 : 1.0, + opacity: widget.entry.isSelected ? 0.0 : 1.0, child: widget.entry.waitingForNetwork ? Container() : Text( @@ -73,13 +68,13 @@ class _FoodEntryWidgetState extends State { ), ), Opacity( - opacity: showCancelAndDelete ? 0.66 : 0.0, + opacity: widget.entry.isSelected ? 0.66 : 0.0, child: Container( color: Theme.of(context).colorScheme.secondary)), ]), ), Opacity( - opacity: showCancelAndDelete ? 1.0 : 0.0, + opacity: widget.entry.isSelected ? 1.0 : 0.0, child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ @@ -87,10 +82,8 @@ class _FoodEntryWidgetState extends State { child: IconButton( padding: const EdgeInsets.all(0.0), icon: const Icon(Icons.cancel), - onPressed: showCancelAndDelete - ? () => setState(() { - showCancelAndDelete = false; - }) + onPressed: widget.entry.isSelected + ? widget.onTap(context, widget.entry) : null, ), ), @@ -100,7 +93,7 @@ class _FoodEntryWidgetState extends State { iconSize: 24, icon: const Icon(Icons.delete), color: Colors.redAccent, - onPressed: showCancelAndDelete + onPressed: widget.entry.isSelected ? () => widget.onDelete(context, widget.entry.id) : null), ), @@ -108,8 +101,9 @@ class _FoodEntryWidgetState extends State { child: IconButton( padding: const EdgeInsets.all(0.0), icon: const Icon(Icons.edit), - onPressed: showCancelAndDelete + onPressed: widget.entry.isSelected ? () async { + widget.onTap(context, widget.entry); await showDialog( context: context, builder: (dialogContext) { @@ -120,9 +114,6 @@ class _FoodEntryWidgetState extends State { }); }, ); - setState(() { - showCancelAndDelete = false; - }); } : null), ), @@ -248,6 +239,7 @@ class _FoodEntryChangeDialogState extends State { mass: mass, kcalPer100: kcalPer100, waitingForNetwork: widget.entry.waitingForNetwork, + isSelected: false, ); widget.onChange(context, newEntry); diff --git a/lib/food_scan/food_fact_lookup.dart b/lib/food_scan/food_fact_lookup.dart index 4ca736a..e0117eb 100644 --- a/lib/food_scan/food_fact_lookup.dart +++ b/lib/food_scan/food_fact_lookup.dart @@ -17,6 +17,7 @@ class FoodFactLookupClient { try { var request = await client.getUrl(Uri.parse(getProductUrl(ean))); + await Future.delayed(const Duration(seconds: 10)); var response = await request.close(); diff --git a/lib/perdate/entry_list.dart b/lib/perdate/entry_list.dart index c139ebe..384808a 100644 --- a/lib/perdate/entry_list.dart +++ b/lib/perdate/entry_list.dart @@ -49,6 +49,11 @@ class FoodEntryList extends StatelessWidget { .read() .add(FoodChangedEvent(newEntry: changedEntry)); }, + onTap: (_, tappedEntry) { + context + .read() + .add(FoodEntryTapped(entryID: tappedEntry.id)); + }, ), const Divider(), ], diff --git a/lib/storage/storage.dart b/lib/storage/storage.dart index b129588..8098ed6 100644 --- a/lib/storage/storage.dart +++ b/lib/storage/storage.dart @@ -64,6 +64,7 @@ class FoodStorage { mass: mass, kcalPer100: kcalPerMass, waitingForNetwork: false, + isSelected: false, ); entries.add(entry); }