Home > Net >  How to switch displaying widgets by each tapping
How to switch displaying widgets by each tapping

Time:09-18

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.

  • Related