Home > Enterprise >  Listview.builder with stateful item is not working properly
Listview.builder with stateful item is not working properly

Time:08-26

I am getting very stange issue while using listview that having stateful items. i also explain one example here.

the example is about cart screen and every cart item has one button to remove the cart item.

The issue is that- i have one Listview.builder(), and in it's itemBuilder - every item is another Stateful Widget, i am passing one function parameter to that widget and that widget has one IconButton and pressing on that i am calling that parameter function. and inside that function i am removing that item from the list. the item is deleted successfully from list but somehow the state is not deleting.

my code is below

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  List<String> list = [
    "Item 1",
    "Item 2",
    "Item 3",
    "Item 4",
    "Item 5",
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("My Cart"),
      ),
      body: ListView.builder(
        itemCount: list.length,
        itemBuilder: (context, index) {
          return CartComponent(
            value: list[index],
            onDelete: () {
              setState(() {
                list.removeAt(index);
              });
            },
          );
        },
      ),
    );
  }
}

class CartComponent extends StatefulWidget {
  final String value;
  final Function onDelete;

  const CartComponent({Key? key, required this.value, required this.onDelete})
      : super(key: key);

  @override
  State<CartComponent> createState() => _CartComponentState();
}

class _CartComponentState extends State<CartComponent> {
  String? name;

  @override
  void initState() {
    super.initState();
    name = widget.value;
  }

  @override
  Widget build(BuildContext context) {
    return Card(
      margin: const EdgeInsets.all(10),
      child: SizedBox(
        height: 80,
        child: Row(
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            Expanded(
              child: Text(
                "$name",
                style: const TextStyle(fontSize: 16),
              ),
            ),
            IconButton(
              onPressed: () {
                widget.onDelete();
              },
              icon: const Icon(Icons.clear),
            ),
          ],
        ),
      ),
    );
  }
}

the item is removed from the list successfully, and list length is also decreasing but from the view aspect, the last element of the list is being removed (but practically from the List, last item is not removing)

CodePudding user response:

Try this, in your CartComponent add this:

final Function(String) onDelete;

and in press button:

onPressed: () {
                widget.onDelete(widget.value);
              },

and in ListView builder do this:

ListView.builder(
        itemCount: list.length,
        itemBuilder: (context, index) {
          return CartComponent(
            key: UniqueKey(), // the very important part 
            value: list[index],
            onDelete: (value) {
              setState(() {
                list.remove(value);
              });
            },
          );
        },
      )

CodePudding user response:

You can directly use widget.value on text Text(widget.value, this will work.

You can change delete function like

or

  final Function(String value) onDelete;

And pass value widget.onDelete(name!);

Now on itemBuilder

return CartComponent(
    key: ValueKey("item ${list[index]}"),
    value: list[index],
    onDelete: (v) {
      list.remove(v);
      print(v);
      setState(() {});
    },
  );
},

For now,

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  List<String> list = [
    "Item 1",
    "Item 1",
    "Item 2",
    "Item 3",
    "Item 3",
    "Item 4",
    "Item 5",
    "Item 5",
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ListView.builder(
        itemCount: list.length,
        itemBuilder: (context, index) {
          return CartComponent(
            key: ValueKey("item ${list[index]}"),
            value: list[index],
            onDelete: () {
              list.removeAt(index);
              setState(() {});
            },
          );
        },
      ),
    );
  }
}

class CartComponent extends StatefulWidget {
  final String value;
  final Function onDelete;

  const CartComponent({
    Key? key,
    required this.value,
    required this.onDelete,
  }) : super(key: key);

  @override
  State<CartComponent> createState() => _CartComponentState();
}

class _CartComponentState extends State<CartComponent> {
  String? value;

  @override
  void initState() {
    super.initState();
    value = widget.value;
  }

  @override
  Widget build(BuildContext context) {
    return Card(
      margin: const EdgeInsets.all(10),
      child: SizedBox(
        height: 80,
        child: Row(
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            Expanded(
              child: Text(
                value!,
                style: const TextStyle(fontSize: 16),
              ),
            ),
            IconButton(
              onPressed: () => widget.onDelete(),
              icon: const Icon(Icons.clear),
            ),
          ],
        ),
      ),
    );
  }
}
  • Related