Search code examples
flutterdartflutter-listview

RangeError in AnimationList Flutter?


I am working on a project in which i am generating an order list. Whenever, i try to delete an item from list. It's throwing an exception of RangeError (index): Invalid value: Only valid value is 0: 1. However, it deletes that item on the same time. If there is range issue why is it deleting that item? Or when does this error generated? Another thing is How can I apply try/catch in specific widget?

Here is the code:

class OrderList extends StatefulWidget {
  @override
  _OrderListState createState() => _OrderListState();
}

class _OrderListState extends State<OrderList> {
  CartList cart = CartList.instance;
  final GlobalKey<AnimatedListState> _key = GlobalKey();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.red,
        elevation: 0.0,
        title: Text('Orders'),
        centerTitle: true,
        actions: <Widget>[
          Padding(
            padding: const EdgeInsets.all(16.0),
            child: Badge(
              child: Icon(Icons.shopping_cart, color: Colors.white, size: 40),
              badgeContent: Text(
                cart.listLength(),
                style: TextStyle(color: Colors.white),
              ),
              badgeColor: Colors.amber,
              toAnimate: true,
              animationType: BadgeAnimationType.fade,
            ),
          ),
        ],
      ),
      body: AnimatedList(
        key: _key,
        initialItemCount: cart.list.length,
        itemBuilder: (context, index, animation) {
          return _buildItem(cart.list[index], animation, index);
        },
      ),
    );
  }

  Widget _buildItem(FoodItem list, Animation<double> animation, int index) {

      return SizeTransition(
        sizeFactor: animation,
        child: Card(
          elevation: 2,
          child: ListTile(
            leading: CircleAvatar(
              backgroundImage: AssetImage('assets/${cart.list[index].img}'),
            ),
            title: Text(cart.list[index].name),
            subtitle: Text(
                '${cart.list[index].quantity} x ${cart.list[index].price} = ${cart.list[index].quantity * cart.list[index].price}'),
            trailing: IconButton(
              icon: Icon(
                Icons.delete,
                color: Colors.red,
              ),
              onPressed: (){
                _removeItem(index);
                setState(() {

                });
              },
            ),
          ),
        ),
      );

  }

  void _removeItem(int index) {
    FoodItem removeItem = cart.list.removeAt(index);
    AnimatedListRemovedItemBuilder builder = (context, animation){
      return _buildItem(removeItem, animation, index);
    };
    _key.currentState.removeItem(index, builder);
  }
}

Solution

  • You can copy paste run full code below
    The default insert/remove animation duration is Duration(milliseconds: 300)
    You can wait like milliseconds: 350

    onPressed: () async{
                  _removeItem(index);
                  await Future.delayed(Duration(milliseconds: 350), () {});
                  setState(() {
                    cart.list.removeAt(index);
                  });
                },
    

    enter image description here

    full code

    import 'package:flutter/material.dart';
    
    class FoodItem {
      String name;
      String img;
      int quantity;
      double price;
    
      FoodItem({this.name, this.img, this.quantity, this.price});
    }
    
    class CartList {
      List<FoodItem> list = [
        FoodItem(
            name: "1",
            img: "https://picsum.photos/250?image=9",
            quantity: 1,
            price: 10.0),
        FoodItem(
            name: "2",
            img: "https://picsum.photos/250?image=10",
            quantity: 2,
            price: 20.0),
        FoodItem(
            name: "3",
            img: "https://picsum.photos/250?image=11",
            quantity: 3,
            price: 30.0),
        FoodItem(
            name: "4",
            img: "https://picsum.photos/250?image=12",
            quantity: 4,
            price: 40.0)
      ];
    }
    
    class OrderList extends StatefulWidget {
      @override
      _OrderListState createState() => _OrderListState();
    }
    
    class _OrderListState extends State<OrderList> {
      CartList cart = CartList();
      final GlobalKey<AnimatedListState> _key = GlobalKey();
    
      @override
      Widget build(BuildContext context) {
        print(cart.list.length);
        return Scaffold(
          appBar: AppBar(
            backgroundColor: Colors.red,
            elevation: 0.0,
            title: Text('Orders'),
            centerTitle: true,
          ),
          body: cart.list.length == 0
              ? Container(child: Text("empty"))
              : AnimatedList(
                  key: _key,
                  initialItemCount: cart.list.length,
                  itemBuilder: (context, index, animation) {
                    print(index);
                    return _buildItem(cart.list[index], animation, index);
                  },
                ),
        );
      }
    
      Widget _buildItem(FoodItem list, Animation<double> animation, int index) {
        return SizeTransition(
          sizeFactor: animation,
          child: Card(
            elevation: 2,
            child: ListTile(
              leading: CircleAvatar(
                backgroundImage: NetworkImage('${cart.list[index].img}'),
              ),
              title: Text(cart.list[index].name),
              subtitle: Text(
                  '${cart.list[index].quantity} x ${cart.list[index].price} = ${cart.list[index].quantity * cart.list[index].price}'),
              trailing: IconButton(
                icon: Icon(
                  Icons.delete,
                  color: Colors.red,
                ),
                onPressed: () async{
                  _removeItem(index);
                  await Future.delayed(Duration(milliseconds: 350), () {});
                  setState(() {
                    cart.list.removeAt(index);
                  });
                },
              ),
            ),
          ),
        );
      }
    
      void _removeItem(int index) {
        //FoodItem removeItem = cart.list.removeAt(index);
        FoodItem removeItem = cart.list[index];
        AnimatedListRemovedItemBuilder builder = (context, animation) {
          return _buildItem(removeItem, animation, index);
        };
        _key.currentState.removeItem(index, builder);
        //cart.list.removeAt(index);
      }
    }
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
            visualDensity: VisualDensity.adaptivePlatformDensity,
          ),
          home: OrderList(),
        );
      }
    }