I want to implement a feature that lets users change information about the items
by tapping each item card.
By default, users can see the price
of the items. When tapping an item card, the user can see the detail
of the item. When tapping the same card one more time, then the user will see the price
of the item.
I am thinking about using GestureDetector
and switching the widgets inside onTap
, but my implementation doesn't work and I can't come up with a solution.
When I write ItemInfo itemInfo = ItemInfo.price;
outside of the buildItemCards
method, switching the displaying widgets works, but every card's display changes.
How can I switch the price
and detail
by tapping each item card?
Item
's model:
class Item {
int id;
String name;
int price;
String detail;
Item({
required this.id,
required this.name,
required this.price,
required this.detail,
});
}
class SwitchWidgetsScreen extends StatefulWidget {
const SwitchWidgetsScreen({
Key? key,
}) : super(key: key);
@override
State<SwitchWidgetsScreen> createState() => _SwitchWidgetsScreenState();
}
List<Item> items = [
Item(id: 1, name: 'Apple', price: 2, detail: 'Juicy.'),
Item(id: 2, name: 'Banana', price: 1, detail: 'Sweet.'),
Item(id: 3, name: 'Chocolate', price: 3, detail: 'Bitter.'),
];
enum ItemInfo {
price,
detail,
}
class _SwitchWidgetsScreenState extends State<SwitchWidgetsScreen> {
Widget buildItemCards(Item item) {
ItemInfo itemInfo = ItemInfo.price;
return GestureDetector(
onTap: () {
setState(() {
if (itemInfo == ItemInfo.price) {
itemInfo = ItemInfo.detail;
} else {
itemInfo = ItemInfo.price;
}
});
},
child: Card(
child: ListTile(
title: Text(item.name),
subtitle: itemInfo == ItemInfo.price
? Text('Price: ${item.price}G')
: Text(item.detail),
),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Items'),
),
body: ListView.builder(
itemCount: items.length,
itemBuilder: (_, index) {
return buildItemCards(items[index]);
},
),
);
}
}
CodePudding user response:
We are trying to change each item separately, but using single variable to control the list. So once the value changes, the full list get changes. You can create another list to control the list or include a variable on model class.
With another list to control text.
class _SwitchWidgetsScreenState extends State<SwitchWidgetsScreen> {
List<ItemInfo> infoTypes =
List.generate(items.length, (index) => ItemInfo.price);
Widget buildItemCards(int index) {
Item item = items[index];
return GestureDetector(
onTap: () {
setState(() {
if (infoTypes[index] == ItemInfo.price) {
infoTypes[index] = ItemInfo.detail;
} else {
infoTypes[index] = ItemInfo.price;
}
});
},
child: Card(
child: ListTile(
title: Text(item.name),
subtitle: infoTypes[index] == ItemInfo.price
? Text('Price: ${item.price}G')
: Text(item.detail),
),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Items'),
),
body: ListView.builder(
itemCount: items.length,
itemBuilder: (_, index) {
return buildItemCards(index);
},
),
);
}
}
Or adding variable or extending Item class
class Item {
int id;
String name;
int price;
String detail;
ItemInfo itemInfo;
Item(
{required this.id,
required this.name,
required this.price,
required this.detail,
this.itemInfo = ItemInfo.price});
}
class _SwitchWidgetsScreenState extends State<SwitchWidgetsScreen> {
Widget buildItemCards(int index) {
Item item = items[index];
return GestureDetector(
onTap: () {
setState(() {
if (items[index].itemInfo == ItemInfo.price) {
items[index].itemInfo = ItemInfo.detail;
} else {
items[index].itemInfo = ItemInfo.price;
}
});
},
child: Card(
child: ListTile(
title: Text(item.name),
subtitle: items[index].itemInfo == ItemInfo.price
? Text('Price: ${item.price}G')
: Text(item.detail),
),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Items'),
),
body: ListView.builder(
itemCount: items.length,
itemBuilder: (_, index) {
return buildItemCards(index);
},
),
);
}
}
I prefer using final variable with copyWith method.