skip to Main Content

I am trying the widgets.AnimatedGrid.1 mysample of AnimatedGrid class documentation and I am always getting a RangeError (index): Invalid value: Not in inclusive range whenever I replace at runtime the late ListModel<int> _list in _AnimatedGridSampleState with a new shorter list.

Simply replacing the code of _insert handler with:

void _insert() {
  setState(() {
    _list = ListModel<int>(
      listKey: _gridKey,
      initialItems: <int>[7, 6, 5],
      removedItemBuilder: _buildRemovedItem,
    );
  });
}

then clicking on + button will throw a RangeError.

Since build() in AnimatedGridSampleState depends of _list I was expecting that it will build a new AnimatedGrid with the correct initialItemCount and avoiding RangeError:

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        appBar: ...,
        body: Padding(
          padding: const EdgeInsets.all(16.0),
          child: AnimatedGrid(
            key: _gridKey,
            initialItemCount: _list.length,
            itemBuilder: _buildItem,
          ),
        ),
      ),
    );
  }

Yet, the _buildItem(...) it is still being called with the same indexes of the former longer _list. Why?


You can try it by yourself running on the browser in the snippet container of AnimatedGrid page, replacing _insert() code just like shown in the following print screens. You will not see the RangeError but you will see that former items 4, 5, 6 remain on the AnimatedGrid.


3

Answers


  1. Chosen as BEST ANSWER

    Solved this problem by setting a new GlobalKey<AnimatedGridState> _gridKey on _insert handler of the OP example, which is now:

    void _insert() {
        setState(() {
          _gridKey = GlobalKey<AnimatedGridState>();
          _list = ListModel<int>(
            listKey: _gridKey,
            initialItems: <int>[7, 6, 5],
            removedItemBuilder: _buildRemovedItem,
          );
        });
      }
    

  2. it would be better if you could share the function responsible for removing the item but i can guess what might be the problem if you are getting this error when removing the last item in the list

    if you are removing an item then you need to be careful about something
    first let’s take a look at the removing function

    E removeAt(int index) {
    final E removedItem = _items.removeAt(index);
    if (removedItem != null) {
      _animatedGrid!.removeItem(
        index,
        (BuildContext context, Animation<double> animation) {
          return removedItemBuilder(removedItem, context, animation);
        },
      );
    }
    return removedItem;
    

    as you can see at the start of the function we are using the index to remove the item we want to remove and storing it in a new variable
    then we are using it here

     _animatedGrid!.removeItem(
    index,
    (BuildContext context, Animation<double> animation) {
      return removedItemBuilder(removedItem, context, animation);
    },);
    

    as you can see we are using the item that we removed from the list because it will be displayed during the animation that’s why we need the item but not the index
    and we can’t use the index directly in this part cause we already removed the item from the list so if we used it like that

     _animatedGrid!.removeItem(
        index,
        (BuildContext context, Animation<double> animation) {
          return removedItemBuilder(_items[index], context, animation);
        },
      );
    

    you will be getting a RangeError (index): Invalid value: Not in inclusive range
    because this item is already removed and so it’s index is out of range

    Login or Signup to reply.
  3. To remove items takes Duration(milliseconds: 300). So setState try to rebuild the items meanwhile and cause the issue. In order to overcome this issue, I came up with removing one by one and then inserting item, created another two method on the ListModel.

    class ListModel<E> {
     .....
      void clear() {
        for (int i = _items.length - 1; i >= 0; i--) {
          removeAt(i);
        }
      }
    
      void addAll(List<E> item) {
        for (int i = 0; i < item.length; i++) {
          insert(i, item[i]);
        }
      }
    

    Now while you like to reset the item.

      void _insert() async {
        _list.clear();
        /// delay to looks good; kDuration takes to remove item, therefore I am using Future method.
        await Future.delayed(const Duration(milliseconds: 300));
        setState(() {
          _list.addAll(<int>[7, 6, 5]);
        });
      }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search