Move select logic for FoodEntry into BLoC
This commit is contained in:
parent
ace03d98d2
commit
f28626c49e
@ -2,7 +2,6 @@ import 'package:calorimeter/storage/storage.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:calorimeter/food_entry/food_entry_bloc.dart';
|
import 'package:calorimeter/food_entry/food_entry_bloc.dart';
|
||||||
import 'package:calorimeter/utils/row_with_spacers_widget.dart';
|
import 'package:calorimeter/utils/row_with_spacers_widget.dart';
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
|
|
||||||
class EnterFoodWidget extends StatefulWidget {
|
class EnterFoodWidget extends StatefulWidget {
|
||||||
final Function(BuildContext context, FoodEntryState entry) onAdd;
|
final Function(BuildContext context, FoodEntryState entry) onAdd;
|
||||||
@ -128,6 +127,7 @@ class _EnterFoodWidgetState extends State<EnterFoodWidget> {
|
|||||||
mass: massAsNumber,
|
mass: massAsNumber,
|
||||||
kcalPer100: kcalPerMassAsNumber,
|
kcalPer100: kcalPerMassAsNumber,
|
||||||
waitingForNetwork: false,
|
waitingForNetwork: false,
|
||||||
|
isSelected: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
widget.onAdd(context, entry);
|
widget.onAdd(context, entry);
|
||||||
|
@ -18,6 +18,7 @@ class FoodEntryBloc extends Bloc<FoodEvent, PageState> {
|
|||||||
on<FoodChangedEvent>(handleFoodChangedEvent);
|
on<FoodChangedEvent>(handleFoodChangedEvent);
|
||||||
on<FoodDeletionEvent>(handleDeleteFoodEvent);
|
on<FoodDeletionEvent>(handleDeleteFoodEvent);
|
||||||
on<BarcodeScanned>(handleBarcodeScannedEvent);
|
on<BarcodeScanned>(handleBarcodeScannedEvent);
|
||||||
|
on<FoodEntryTapped>(handleFoodEntryTapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleFoodEntryEvent(
|
void handleFoodEntryEvent(
|
||||||
@ -74,12 +75,23 @@ class FoodEntryBloc extends Bloc<FoodEvent, PageState> {
|
|||||||
|
|
||||||
List<FoodEntryState> newList = List.from(state.foodEntries);
|
List<FoodEntryState> newList = List.from(state.foodEntries);
|
||||||
var newEntryWaiting = FoodEntryState(
|
var newEntryWaiting = FoodEntryState(
|
||||||
kcalPer100: 0, name: "", mass: 0, waitingForNetwork: true);
|
kcalPer100: 0,
|
||||||
|
name: "",
|
||||||
|
mass: 0,
|
||||||
|
waitingForNetwork: true,
|
||||||
|
isSelected: false,
|
||||||
|
);
|
||||||
newList.add(newEntryWaiting);
|
newList.add(newEntryWaiting);
|
||||||
emit(PageState(foodEntries: newList));
|
emit(PageState(foodEntries: newList));
|
||||||
|
|
||||||
await responseFuture.then((response) {
|
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) {
|
if (response.status == FoodFactResponseStatus.barcodeNotFound) {
|
||||||
emit(PageState(
|
emit(PageState(
|
||||||
@ -100,11 +112,29 @@ class FoodEntryBloc extends Bloc<FoodEvent, PageState> {
|
|||||||
mass: response.food?.mass ?? 0,
|
mass: response.food?.mass ?? 0,
|
||||||
kcalPer100: response.food?.kcalPer100g ?? 0,
|
kcalPer100: response.food?.kcalPer100g ?? 0,
|
||||||
waitingForNetwork: false,
|
waitingForNetwork: false,
|
||||||
|
isSelected: false,
|
||||||
);
|
);
|
||||||
newList.add(newEntryFinishedWaiting);
|
|
||||||
|
newList.removeAt(index);
|
||||||
|
newList.insert(index, newEntryFinishedWaiting);
|
||||||
emit(PageState(foodEntries: newList));
|
emit(PageState(foodEntries: newList));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void handleFoodEntryTapped(
|
||||||
|
FoodEntryTapped event, Emitter<PageState> 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 {}
|
class FoodEvent {}
|
||||||
@ -133,6 +163,12 @@ class BarcodeScanned extends FoodEvent {
|
|||||||
BarcodeScanned({required this.scanResultFuture});
|
BarcodeScanned({required this.scanResultFuture});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class FoodEntryTapped extends FoodEvent {
|
||||||
|
final String entryID;
|
||||||
|
|
||||||
|
FoodEntryTapped({required this.entryID});
|
||||||
|
}
|
||||||
|
|
||||||
/// This is the state for one date/page
|
/// This is the state for one date/page
|
||||||
class PageState {
|
class PageState {
|
||||||
final List<FoodEntryState> foodEntries;
|
final List<FoodEntryState> foodEntries;
|
||||||
@ -159,12 +195,14 @@ class FoodEntryState {
|
|||||||
final int kcalPer100;
|
final int kcalPer100;
|
||||||
final String id;
|
final String id;
|
||||||
final bool waitingForNetwork;
|
final bool waitingForNetwork;
|
||||||
|
bool isSelected;
|
||||||
|
|
||||||
factory FoodEntryState({
|
factory FoodEntryState({
|
||||||
required name,
|
required name,
|
||||||
required mass,
|
required mass,
|
||||||
required kcalPer100,
|
required kcalPer100,
|
||||||
required waitingForNetwork,
|
required waitingForNetwork,
|
||||||
|
required isSelected,
|
||||||
}) {
|
}) {
|
||||||
return FoodEntryState.withID(
|
return FoodEntryState.withID(
|
||||||
id: const Uuid().v1(),
|
id: const Uuid().v1(),
|
||||||
@ -172,6 +210,7 @@ class FoodEntryState {
|
|||||||
mass: mass,
|
mass: mass,
|
||||||
kcalPer100: kcalPer100,
|
kcalPer100: kcalPer100,
|
||||||
waitingForNetwork: waitingForNetwork,
|
waitingForNetwork: waitingForNetwork,
|
||||||
|
isSelected: isSelected,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,8 +220,12 @@ class FoodEntryState {
|
|||||||
required this.mass,
|
required this.mass,
|
||||||
required this.kcalPer100,
|
required this.kcalPer100,
|
||||||
required this.waitingForNetwork,
|
required this.waitingForNetwork,
|
||||||
|
required this.isSelected,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
set selected(bool selected) => isSelected = selected;
|
||||||
|
bool get selected => isSelected;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
//we use quotation marks around the name because the name might contain
|
//we use quotation marks around the name because the name might contain
|
||||||
|
@ -6,12 +6,14 @@ class FoodEntryWidget extends StatefulWidget {
|
|||||||
final FoodEntryState entry;
|
final FoodEntryState entry;
|
||||||
final Function(BuildContext context, String id) onDelete;
|
final Function(BuildContext context, String id) onDelete;
|
||||||
final Function(BuildContext context, FoodEntryState entry) onChange;
|
final Function(BuildContext context, FoodEntryState entry) onChange;
|
||||||
|
final Function(BuildContext context, FoodEntryState entry) onTap;
|
||||||
|
|
||||||
const FoodEntryWidget({
|
const FoodEntryWidget({
|
||||||
super.key,
|
super.key,
|
||||||
required this.entry,
|
required this.entry,
|
||||||
required this.onDelete,
|
required this.onDelete,
|
||||||
required this.onChange,
|
required this.onChange,
|
||||||
|
required this.onTap,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -19,22 +21,15 @@ class FoodEntryWidget extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _FoodEntryWidgetState extends State<FoodEntryWidget> {
|
class _FoodEntryWidgetState extends State<FoodEntryWidget> {
|
||||||
late bool showCancelAndDelete;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
showCancelAndDelete = false;
|
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () {
|
onTap: () => widget.onTap(context, widget.entry),
|
||||||
setState(() {
|
|
||||||
showCancelAndDelete = !showCancelAndDelete;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
@ -51,14 +46,14 @@ class _FoodEntryWidgetState extends State<FoodEntryWidget> {
|
|||||||
: Text(widget.entry.mass.ceil().toString(),
|
: Text(widget.entry.mass.ceil().toString(),
|
||||||
textAlign: TextAlign.end),
|
textAlign: TextAlign.end),
|
||||||
Opacity(
|
Opacity(
|
||||||
opacity: showCancelAndDelete ? 0.0 : 1.0,
|
opacity: widget.entry.isSelected ? 0.0 : 1.0,
|
||||||
child: widget.entry.waitingForNetwork
|
child: widget.entry.waitingForNetwork
|
||||||
? Container()
|
? Container()
|
||||||
: Text(widget.entry.kcalPer100.ceil().toString(),
|
: Text(widget.entry.kcalPer100.ceil().toString(),
|
||||||
textAlign: TextAlign.end),
|
textAlign: TextAlign.end),
|
||||||
),
|
),
|
||||||
Opacity(
|
Opacity(
|
||||||
opacity: showCancelAndDelete ? 0.0 : 1.0,
|
opacity: widget.entry.isSelected ? 0.0 : 1.0,
|
||||||
child: widget.entry.waitingForNetwork
|
child: widget.entry.waitingForNetwork
|
||||||
? Container()
|
? Container()
|
||||||
: Text(
|
: Text(
|
||||||
@ -73,13 +68,13 @@ class _FoodEntryWidgetState extends State<FoodEntryWidget> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Opacity(
|
Opacity(
|
||||||
opacity: showCancelAndDelete ? 0.66 : 0.0,
|
opacity: widget.entry.isSelected ? 0.66 : 0.0,
|
||||||
child: Container(
|
child: Container(
|
||||||
color: Theme.of(context).colorScheme.secondary)),
|
color: Theme.of(context).colorScheme.secondary)),
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
Opacity(
|
Opacity(
|
||||||
opacity: showCancelAndDelete ? 1.0 : 0.0,
|
opacity: widget.entry.isSelected ? 1.0 : 0.0,
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
@ -87,10 +82,8 @@ class _FoodEntryWidgetState extends State<FoodEntryWidget> {
|
|||||||
child: IconButton(
|
child: IconButton(
|
||||||
padding: const EdgeInsets.all(0.0),
|
padding: const EdgeInsets.all(0.0),
|
||||||
icon: const Icon(Icons.cancel),
|
icon: const Icon(Icons.cancel),
|
||||||
onPressed: showCancelAndDelete
|
onPressed: widget.entry.isSelected
|
||||||
? () => setState(() {
|
? widget.onTap(context, widget.entry)
|
||||||
showCancelAndDelete = false;
|
|
||||||
})
|
|
||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -100,7 +93,7 @@ class _FoodEntryWidgetState extends State<FoodEntryWidget> {
|
|||||||
iconSize: 24,
|
iconSize: 24,
|
||||||
icon: const Icon(Icons.delete),
|
icon: const Icon(Icons.delete),
|
||||||
color: Colors.redAccent,
|
color: Colors.redAccent,
|
||||||
onPressed: showCancelAndDelete
|
onPressed: widget.entry.isSelected
|
||||||
? () => widget.onDelete(context, widget.entry.id)
|
? () => widget.onDelete(context, widget.entry.id)
|
||||||
: null),
|
: null),
|
||||||
),
|
),
|
||||||
@ -108,8 +101,9 @@ class _FoodEntryWidgetState extends State<FoodEntryWidget> {
|
|||||||
child: IconButton(
|
child: IconButton(
|
||||||
padding: const EdgeInsets.all(0.0),
|
padding: const EdgeInsets.all(0.0),
|
||||||
icon: const Icon(Icons.edit),
|
icon: const Icon(Icons.edit),
|
||||||
onPressed: showCancelAndDelete
|
onPressed: widget.entry.isSelected
|
||||||
? () async {
|
? () async {
|
||||||
|
widget.onTap(context, widget.entry);
|
||||||
await showDialog(
|
await showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (dialogContext) {
|
builder: (dialogContext) {
|
||||||
@ -120,9 +114,6 @@ class _FoodEntryWidgetState extends State<FoodEntryWidget> {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
setState(() {
|
|
||||||
showCancelAndDelete = false;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
: null),
|
: null),
|
||||||
),
|
),
|
||||||
@ -248,6 +239,7 @@ class _FoodEntryChangeDialogState extends State<FoodEntryChangeDialog> {
|
|||||||
mass: mass,
|
mass: mass,
|
||||||
kcalPer100: kcalPer100,
|
kcalPer100: kcalPer100,
|
||||||
waitingForNetwork: widget.entry.waitingForNetwork,
|
waitingForNetwork: widget.entry.waitingForNetwork,
|
||||||
|
isSelected: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
widget.onChange(context, newEntry);
|
widget.onChange(context, newEntry);
|
||||||
|
@ -17,6 +17,7 @@ class FoodFactLookupClient {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
var request = await client.getUrl(Uri.parse(getProductUrl(ean)));
|
var request = await client.getUrl(Uri.parse(getProductUrl(ean)));
|
||||||
|
await Future.delayed(const Duration(seconds: 10));
|
||||||
|
|
||||||
var response = await request.close();
|
var response = await request.close();
|
||||||
|
|
||||||
|
@ -49,6 +49,11 @@ class FoodEntryList extends StatelessWidget {
|
|||||||
.read<FoodEntryBloc>()
|
.read<FoodEntryBloc>()
|
||||||
.add(FoodChangedEvent(newEntry: changedEntry));
|
.add(FoodChangedEvent(newEntry: changedEntry));
|
||||||
},
|
},
|
||||||
|
onTap: (_, tappedEntry) {
|
||||||
|
context
|
||||||
|
.read<FoodEntryBloc>()
|
||||||
|
.add(FoodEntryTapped(entryID: tappedEntry.id));
|
||||||
|
},
|
||||||
),
|
),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
],
|
],
|
||||||
|
@ -64,6 +64,7 @@ class FoodStorage {
|
|||||||
mass: mass,
|
mass: mass,
|
||||||
kcalPer100: kcalPerMass,
|
kcalPer100: kcalPerMass,
|
||||||
waitingForNetwork: false,
|
waitingForNetwork: false,
|
||||||
|
isSelected: false,
|
||||||
);
|
);
|
||||||
entries.add(entry);
|
entries.add(entry);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user