'Object changes in child class widget traverse back to parent class widget where it was initialized
I am working on cart item in Flutter application. Right now I am working on editing cart item. The structure of my code is such that each cart item is a StatefulWidget. When I click on edit cart item I am showing a custom dialog box with extras, being selected for that item. For that purpose I am passing an object of cart item to dialog box and retrieving data to show in dialog box.
The issue I am facing in this case is that If I select some other extras in a dialog box while editing cart item and then close the dialog to discard new changes it still reflect those new changes in selected cart item. But the fact is I am not updating that cart item in case of discarding those new changes.
Here are my some code snippets. In the code below I am calling the extras dialog box from cart item widget class:
Future modifierDialog() {
return showGeneralDialog(
context: context,
barrierDismissible: false,
barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel,
barrierColor: Colors.black45,
transitionDuration: const Duration(milliseconds: 200),
pageBuilder: (BuildContext buildContext, Animation animation,
Animation secondaryAnimation) {
return Dialog(
child: ModifiersDialogWidget(
menuItem: item.copyWith(),
setSelection: (selectedItem, extras) {
HotKeysWidget.of(context)!.editCartItem(selectedItem, extras);
},
editItem: true,
),
);
},
);
}
This is my custom dialog box code of extras. In ModifiersWidget I am actually showing all extras and selecting/unselecting extras.
class ModifiersDialogWidget extends StatefulWidget {
final Food menuItem;
final Function(Food, List<Map<String, dynamic>>) setSelection; /////to save changes + add to cart
final bool editItem; ///////null if it is not edit cart item
const ModifiersDialogWidget({
required this.menuItem,
required this.setSelection,
required this.editItem,
});
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return ModifiersDialogWidgetState();
}
}
class ModifiersDialogWidgetState extends State<ModifiersDialogWidget> {
late Future<Map<String, dynamic>> data;
bool changesMade = false;
final GlobalKey<ModifiersWidgetState> modifierKey = GlobalKey();
@override
void initState() {
super.initState();
data = fetchTimeline();
}
Future<Map<String, dynamic>> fetchTimeline() async {
/////////////COUNT TOTAL NUMBER OF REQUIRED MODIFIERS
int totalRequiredModifiers = 0;
for (var object in widget.menuItem.modifiers) {
if (object.required) ++totalRequiredModifiers;
object.selected = false;
}
widget.menuItem.totalPrice = widget.menuItem.price;
/////////////SET FOR SELECTED MODIFIERS AND ADD THEN TO LIST
List<Map<String, dynamic>> selectedExtras = [];
if (widget.editItem) {
for (var modifier in widget.menuItem.selectedModifiers) {
int modIndex = widget.menuItem.modifiers
.indexWhere((element) => element.id == modifier.id);
widget.menuItem.modifiers[modIndex].selected = true;
for (var extra in modifier.extras) {
int extraIndex = widget.menuItem.modifiers[modIndex].extras
.indexWhere((element) => element.id == extra.id);
widget.menuItem.modifiers[modIndex].extras[extraIndex].selected =
true;
selectedExtras.add(
{
'extra_id': extra.id,
'extraname': extra.name,
'price': extra.originalPrice,
},
);
widget.menuItem.totalPrice =
widget.menuItem.totalPrice + extra.originalPrice;
}
}
}
return {
'foodItem': Food(
widget.menuItem.id.toString(),
widget.menuItem.name,
widget.menuItem.price,
widget.menuItem.price,
widget.menuItem.image,
widget.menuItem.description,
widget.menuItem.ingredients,
1,
widget.menuItem.chefSpecial,
widget.menuItem.off,
widget.menuItem.totalPrice,
0.0,
widget.menuItem.totalExtraPrice,
widget.menuItem.available,
[],
[],
widget.menuItem.printerID,
false,
false,
false,
widget.menuItem.extrasIDS,
widget.menuItem.modifiers,
widget.menuItem.selectedModifiers,
widget.menuItem.modifierExist,
widget.menuItem.openPrice,
'false',
'none',
false,
'',
0.0,
widget.menuItem.GST,
0.0,
0.0,
0.0,
0.0,
0,
'',
'',
0.0,
0.0,
0.0,
0.0,
[],
null,
),
'totalRequired': totalRequiredModifiers,
'selectedExtras': selectedExtras,
};
}
@override
Widget build(BuildContext context) {
return Container(
width: MediaQuery.of(context).size.width * 0.70,
height: MediaQuery.of(context).size.height * 0.90,
child: Column(
children: [
Container(
width: double.infinity,
color: Colors.grey[400],
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 10),
alignment: Alignment.center,
child: Row(
children: [
Expanded(
child: Container(
alignment: Alignment.center,
child: Text(
'Modifiers',
style: AppTextStyles().style2,
),
),
),
IconButton(
onPressed: () {
Navigator.of(context).pop();
},
padding: EdgeInsets.zero,
icon: Icon(
Icons.close,
color: Colors.white,
size: 35,
),
),
],
),
),
Expanded(
child: Container(
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 10),
child: FutureBuilder(
future: data,
builder:
(context, AsyncSnapshot<Map<String, dynamic>> snapshot) {
return snapshot.connectionState == ConnectionState.waiting
? const Center(child: CircularProgressIndicator())
: ModifiersWidget(
key: modifierKey,
data: snapshot.data,
setSelection: (item, extras) {
widget.setSelection(item, extras);
},
showConfirmation: () {
changesMade = true;
},
);
},
),
),
),
],
),
);
}
}
class ModifiersWidget extends StatefulWidget {
final Map<String, dynamic>? data;
Function(Food, List<Map<String, dynamic>>) setSelection;
VoidCallback showConfirmation;
ModifiersWidget({
Key? key,
required this.data,
required this.setSelection,
required this.showConfirmation,
}) : super(key: key);
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return ModifiersWidgetState();
}
}
class ModifiersWidgetState extends State<ModifiersWidget> {
bool status = true;
bool isLoading = false;
late List<Modifier> modifiers;
late Modifier currentModifier;
final GlobalKey<ModifierOptionsState> optionsKeys = GlobalKey();
final ScrollController _scrollController = ScrollController();
List<String> requiredFilledModifiers = [];
late Food menuItem;
late String errorTxt;
List<Map<String, dynamic>> extraItems = [];
bool changesMade = false;
@override
void initState() {
errorTxt = '';
initData();
super.initState();
}
initData() {
menuItem = widget.data!['foodItem'];
menuItem.GSTPrice = (menuItem.totalPrice * (menuItem.GST / 100));
modifiers = menuItem.modifiers;
currentModifier = modifiers[0];
currentModifier.selected = true;
if (menuItem.selectedModifiers.isNotEmpty) {
for (int i = 0; i < menuItem.selectedModifiers.length; i++) {
requiredFilledModifiers.add(menuItem.selectedModifiers[i].id);
}
print('SELECTED MODIFIER IN STATE CLASS: $requiredFilledModifiers');
}
extraItems = widget.data!['selectedExtras'];
}
}
This is my model class:
class Food {
String id;
String name;
double
price; ///////////ORIGINAL PRICE OF ITEM SINGLE ITEM EXCLUDING EXTRAS IF EXIST
double
singleItemPrice; //////SINGLE QUAN PRICE OF CART ITEM IN HOTKEYS INCLUDING EXTRAS IF EXIST
String image;
String description;
String ingredients;
int quantity;
bool chefSpecial;
double off;
double totalPrice; /////INCLUDES GST AND EXTRAS IF EXIST
double cost; ///////TOTAL PRICE OF ITEM - TOTAL GST PRICE
double
totalExtraPrice; //////////////////TOTAL EXTRA PRICE SELECTED FOR ITEM IN CART
bool available;
List<Extra>
extras; //////////EXTRAS WHICH HAVE BEEN SELECTED FOR THE ITEM (FOR ORDER USE ONLY)
List<String> categoryName;
String printerID;
bool kioskValue;
bool dineinValue;
bool mobileValue;
List<String> extrasIDS;
List<Modifier> modifiers;
List<Modifier> selectedModifiers;
bool modifierExist;
bool openPrice;
String hidden;
String color;
bool selected;
String note;
double GSTPrice; /////TOTAL GST FOR MORE THAN ONE ITEM IN CART
double GST; ////////SINGLE GST VALUE
double perDiscount;
///AMOUNT OF % DISC IF GIVEN ON ITEM IN HOTKEYS
double dollarDiscount; //AMOUNT OF $ DISC IFF GIVEN ON ITEM IN HOTKEYS
double
overridePrice; ////// THE NEW OVERRIDE PRICE AFTER +/- ORIGINAL ITEM PRICE
double newDiscPrice; //AMOUNT OF DISC GIVEN ON ITEM EITHER % DISC OR $ DISC
int cartCount;
///QUAN OF ITEM IN CART
String UPC;
String productID;
double per2;
double per3;
double per4;
double per5;
List<Allergy> allergies;
Deal?
dealItem; /////////////IF MENU ITEM TYPE IS DEAL THEN ASSIGN IT OTHERWISE IT IS NULL VALUE
Food copyWith() => Food(
this.id,
this.name,
this.price,
this.singleItemPrice,
this.image,
this.description,
this.ingredients,
this.quantity,
this.chefSpecial,
this.off,
this.totalPrice,
this.cost,
this.totalExtraPrice,
this.available,
this.extras,
this.categoryName,
this.printerID,
this.kioskValue,
this.dineinValue,
this.mobileValue,
this.extrasIDS,
this.modifiers,
this.selectedModifiers,
this.modifierExist,
this.openPrice,
this.hidden,
this.color,
this.selected,
this.note,
this.GSTPrice,
this.GST,
this.dollarDiscount,
this.perDiscount,
this.overridePrice,
this.newDiscPrice,
this.cartCount,
this.UPC,
this.productID,
this.per2,
this.per3,
this.per4,
this.per5,
this.allergies,
this.dealItem,
);
Food(
this.id,
this.name,
this.price,
this.singleItemPrice,
this.image,
this.description,
this.ingredients,
this.quantity,
this.chefSpecial,
this.off,
this.totalPrice,
this.cost,
this.totalExtraPrice,
this.available,
this.extras,
this.categoryName,
this.printerID,
this.kioskValue,
this.dineinValue,
this.mobileValue,
this.extrasIDS,
this.modifiers,
this.selectedModifiers,
this.modifierExist,
this.openPrice,
this.hidden,
this.color,
this.selected,
this.note,
this.GSTPrice,
this.GST,
this.dollarDiscount,
this.perDiscount,
this.overridePrice,
this.newDiscPrice,
this.cartCount,
this.UPC,
this.productID,
this.per2,
this.per3,
this.per4,
this.per5,
this.allergies,
this.dealItem,
);
I didn't see any reason of traversing back the changes being made in ModifiersWidget to ModifiersDialogWidget widget and also the cart item widget itseld.
Anyone kindly help me with this problem.
Thanks in advance
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
