Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter Checkbox wrong animation inside a ReorderableListView

I'm must doing something wrong, but when I reorder a checked checkbox inside a ReorderableListView, it is animating the unchecked tile:

enter image description here

Here a sample code that I'm using:

ReorderableListView(
  padding: const EdgeInsets.symmetric(horizontal: 40),
  children: <Widget>[
    for (int index = 0; index < _items.length; index++)
      ListTile(
        leading: Checkbox(
          
          key: Key('$index'),
          onChanged: (v) => null,
          value: _items[index].isOdd ? true : false,
        ),
        key: Key('$index'),
        tileColor: _items[index].isOdd ? oddItemColor : evenItemColor,
        title: Text('Item ${_items[index]}'),
      ),
  ],
  onReorder: (int oldIndex, int newIndex) {
    setState(() {
      if (oldIndex < newIndex) {
        newIndex -= 1;
      }
      final int item = _items.removeAt(oldIndex);
      _items.insert(newIndex, item);
    });

Full code here!

How can I do it properly? Cheers!

like image 714
Jonas Cerqueira Avatar asked May 03 '26 01:05

Jonas Cerqueira


2 Answers

I've just realised my mistake, using key: Key('$index') is wrong because it is changing onReorder, so flutter is matching my old checked Checkbox value with the new unchecked Checkbox(My bad =|). Just use an ObjectKey in the ListTile and it will match correctly!

ListTile(
        leading: Checkbox(
          onChanged: (v) => null,
          value: _items[index].isOdd ? true : false,
        ),
        key: ObjectKey(_items[index]),
        tileColor: _items[index].isOdd ? oddItemColor : evenItemColor,
        title: Text('Item ${_items[index]}'),
      ),
like image 97
Jonas Cerqueira Avatar answered May 04 '26 13:05

Jonas Cerqueira


First off, you only need a key on ListTile, as they are the direct descendants of the ReorderableListView.

Second, the key needs to non-index-based. You can use UniqueKey or GlobalKey, but the best practice is to use a ValueKey, passing the value at that index.

In your case:

class _MyStatefulWidgetState extends State<MyStatefulWidget> {
  final List<int> _items = List<int>.generate(2, (int index) => index);

  @override
  Widget build(BuildContext context) {
    return ReorderableListView(
      padding: const EdgeInsets.symmetric(horizontal: 40),
      children: <Widget>[
        for (int index = 0; index < _items.length; index++)
          ListTile(
            leading: Checkbox(
              onChanged: (v) => null,
              value: _items[index].isOdd ? true : false,
            ),
            // here
            key: ValueKey(_items[index]),
            tileColor: _items[index].isOdd ? oddItemColor : evenItemColor,
            title: Text('Item ${_items[index]}'),
          ),
      ],
      onReorder: (int oldIndex, int newIndex) {
        setState(() {
          if (oldIndex < newIndex) {
            newIndex -= 1;
          }
          final int item = _items.removeAt(oldIndex);
          _items.insert(newIndex, item);
        });
      },
    );
  }
}

As for why your indexed keys don't work - my understanding is that after the widget rebuilds (from reordering the list), your ListTile is keyed at the wrong index (because the index of that ListTile has changed). When you use the value, that is a constant that can be used to identify the widget.

Emily Fortuna on Medium has written a wonderful article on keys that I would highly recommend reading if you are going to continue developing with Flutter.

like image 31
Alex Hartford Avatar answered May 04 '26 15:05

Alex Hartford



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!