deleting an element always delete the last one in flutter

791 views Asked by At
List storedWishes = [
      {
        'title': 'Phone',
        'description': 'a phone would be nice',
        'price': 200.00,
        'icon': Icon(Icons.phone)
      },
      {
        'title': 'monitor',
        'description': 'need it for a 2 screen setup',
        'price': 350.00,
        'icon': Icon(Icons.tv)
      },
      {
        'title': 'headphones',
        'description': 'need some high quality sound',
        'price': 100.00,
        'icon': Icon(Icons.headset)
      },
    ];
    final wishes = useState(storedWishes);

    return MaterialApp(
        home: Scaffold(
            appBar: AppBar(
              title: const Text('iWant'),
            ),
            body: Column(
              //map the wishes to a list of wish widgets
              children: wishes.value
                  .map((wish) => Wish(
                        title: wish['title'] as String,
                        description: wish['description'] as String,
                        price: wish['price'] as double,
                        icon: wish['icon'],
                        onDelete: () {
                          wishes.value.remove(wish)
                          wishes.notifyListeners();
                        },
                      ))
                  .toList(),
            )));

wish.dart code:

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

class Wish extends HookWidget {
  const Wish(
      {Key? key,
      this.title = 'default title',
      this.description = 'default description',
      this.price = 0.00,
      this.icon = const Icon(Icons.star),
      this.onDelete})
      : super(key: key);
  final String title;
  final String description;
  final double price;
  final dynamic icon;
  final VoidCallback? onDelete;
  @override
  Widget build(BuildContext context) {
    var wishes = useState({
      'title': title,
      'description': description,
      'price': price,
      'image': icon
    });
    return Card(
        child: Row(children: [
      wishes.value['image'] as Icon,
      Expanded(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(wishes.value['title'] as String),
            Text(wishes.value['description'] as String),
          ],
        ),
      ),
      Text((wishes.value['price'].toString()) + '\$'),
      Column(
        children: [
          Icon(Icons.edit),
          IconButton(
            onPressed: () {
              onDelete!();
            },
            icon: Icon(Icons.delete),
          )
        ],
      ),
    ]));
  }
}
    //   child: Card(
    //       child: Row(children: [
    //     ,
    //     Column(
    //       crossAxisAlignment: CrossAxisAlignment.start,
    //       children: [
    //         Text('Product name'),
    //         Text('Product description'),
    //       ],
    //     ),
    //     Column(
    //       crossAxisAlignment: CrossAxisAlignment.end,
    //       children: [
    //         Text('Price:'),
    //         Text('21\$'),
    //       ],
    //     ),
    //   ])),
    // );

clicking the delete button always removes last element when i click on delete of any other element

edit1: i've debugPrinted the array after clicking on delete icon and the array is edited correctly but the wrong widget get removed from the screen

here's a video: https://drive.google.com/file/d/1bEjHh4utynZk3QzSWw5gWv__YcpqKkb2/view?usp=sharing

edit2: I've removed hooks from the wish widget and that seems to work properly now ? anyway thanks for the help :)

3

There are 3 answers

2
Ivo On BEST ANSWER

I don't know why it's not working for you, but it works fine for me. Since I don't know what your Wish class looks like I made it like this:

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
    
void main() {
  runApp(MyApp());
}

class MyApp extends HookWidget {
  @override
  Widget build(BuildContext context) {


List storedWishes = [
      {
        'title': 'Phone',
        'description': 'a phone would be nice',
        'price': 200.00,
        'icon': Icon(Icons.phone)
      },
      {
        'title': 'monitor',
        'description': 'need it for a 2 screen setup',
        'price': 350.00,
        'icon': Icon(Icons.tv)
      },
      {
        'title': 'headphones',
        'description': 'need some high quality sound',
        'price': 100.00,
        'icon': Icon(Icons.headset)
      },
    ];
    final wishes = useState(storedWishes);

    return MaterialApp(
        home: Scaffold(
            appBar: AppBar(
              title: const Text('iWant'),
            ),
            body: Column(
              //map the wishes to a list of wish widgets
              children: wishes.value
                  .map((wish) => TextButton(
                        child: Text(wish['title'] as String),
                        onPressed: () {
                          wishes.value.remove(wish);
                          wishes.notifyListeners();
                        },
                      ))
                  .toList(),
            )));
  }
}

And it works correctly like this.

Edit: I could reproduce your issue using your Wish code. I'm actually not familiar with HookWidget, but changing the code to this seems to fix it. I don't know what useState is exactly used for but leaving it out fixes it. you can even let Wish then extend just StatelessWidget instead of HookWidget

class Wish extends StatelessWidget {
  const Wish(
      {Key? key,
      this.title = 'default title',
      this.description = 'default description',
      this.price = 0.00,
      this.icon = const Icon(Icons.star),
      this.onDelete})
      : super(key: key);
  final String title;
  final String description;
  final double price;
  final dynamic icon;
  final VoidCallback? onDelete;
  @override
  Widget build(BuildContext context) {
    return Card(
        child: Row(children: [
      icon,
      Expanded(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(title),
            Text(description),
          ],
        ),
      ),
      Text((price.toString()) + '\$'),
      Column(
        children: [
          Icon(Icons.edit),
          IconButton(
            onPressed: onDelete,
            icon: Icon(Icons.delete),
          )
        ],
      ),
    ]));
  }
}
2
vineet kumar On

adding a key in listview solved my problem
key: Key(myList.length.toString()),

Example

 ListView.builder(
shrinkWrap: true,
key: Key(controller.filteredList.length.toString()),
physics: ScrollPhysics(),
itemCount:  controller.filteredList.length
itemBuilder: (bc, i) {….your code….}
)
1
AshishB On

This is because it doesn't actually identify the element based on its contents. When you call the remove function on the list, it sees that the elements are a Map object and your input is also a Map object. So it simply deletes the last element.

What you would want to do is assign a unique identifier to each wish. Something like so:

      {
        'id': '0'
        'title': 'headphones',
        'description': 'need some high quality sound',
        'price': 100.00,
        'icon': Icon(Icons.headset)
      },

or if the title is going to be unique for each item then you can also reference it.

And then simply do a deep comparison by editing your delete function as such:

    onDelete: () {
      wishes.value.removeWhere((e) => e["id"] == wish["id"]);
      //OR
      //wishes.value.removeWhere((e) => e["title"] == wish["title"]);
      wishes.notifyListeners();
    },