androidfluttersnapshot

RangeError (index): Invalid value: Only valid value is 0: 2 - Flutter


I am trying to fetch a list from API. It shows the error-

RangeError (index): Invalid value: Only valid value is 0: 2

I am practicing fetch api. Can you please tell me what's the issue with my code and how to avoid showing that red screen error in my app?

This is Code-

    RefreshIndicator(
                onRefresh: () {
                  setState(() {});
                  return fetchOpenMenuList(widget.product);
                },
                child: FutureBuilder<MenuListData>(
                  future: futureOpenMenuList,
                  builder: (context, snapshot) {
                    if (snapshot.hasData) {
                      return ListView.builder(
                        padding: EdgeInsets.symmetric(vertical: 8.h),
                        itemCount: snapshot.data!.data.length,
                        itemBuilder: (BuildContext context, int index) {
                          return ListTile(
                            leading: Image.network(
                              snapshot.data!.data[index]!.items[index].thumb.toString()
                              ),
                            title: Text(snapshot.data!.data[index]!.items[index].prodName),
                            subtitle: Text(snapshot.data!.data[index]!.name),
                            trailing: Container(
                                padding: EdgeInsets.all(5.r),
                                decoration: BoxDecoration(
                                  color: Colors.transparent,
                                  borderRadius: BorderRadius.circular(10.r),
                                ),
                                child: Image.asset("assets/images/MenuIcon.png", height: 20.h, width: 20.w)
                            ),
                          );
                        },
                      );

                    } else if (snapshot.hasError) {
                      return Center(child: Text('No Data Found'));
                    }
                    return const Center(
                      child: SizedBox(
                        height: 50.0,
                        width: 50.0,
                        child: CircularProgressIndicator(),
                      ),
                    );
                  },
                ),
              ),

This is my JSON file-

{
    "ignore": 0,
    "code": 1,
    "message": "OK",
    "data": [
        {
            "ctg_id": "1",
            "name": "Fusion",
            "items": [
                {
                    "prod_id": "1",
                    "prod_name": "Italian Sev Puri",
                    "thumb": "http:\/\/www.galacaterers.in\/images\/menu-items\/thumb-1459925145.jpg"
                }
            ]
        },
        {
            "ctg_id": "5",
            "name": "Cake And Pastries",
            "items": [
                {
                    "prod_id": "57",
                    "prod_name": "Molt And Magic",
                    "thumb": "http:\/\/www.galacaterers.in\/images\/menu-items\/thumb-1459945416.jpg"
                },
                {
                    "prod_id": "49",
                    "prod_name": "Chocolate Zuzups",
                    "thumb": "http:\/\/www.galacaterers.in\/images\/menu-items\/thumb-1459945068.jpg"
                }
            ]
        },
        {
            "ctg_id": "6",
            "name": "Chaat",
            "items": [
                {
                    "prod_id": "99",
                    "prod_name": "Makai Roll Chaat",
                    "thumb": "http:\/\/www.galacaterers.in\/images\/menu-items\/thumb-1459966275.jpg"
                }
            ]
        },
        {
            "ctg_id": "46",
            "name": "Sweet Bite",
            "items": [
                {
                    "prod_id": "23",
                    "prod_name": "Fruit Wati",
                    "thumb": "http:\/\/www.galacaterers.in\/images\/menu-items\/thumb-1459942869.jpg"
                }
            ]
        }
    ]
}

The following is the screenshot of the error:

What I want:

This is Data Model-

import 'dart:convert';

MenuListData menuListDataFromJson(String str) => MenuListData.fromJson(json.decode(str));

String menuListDataToJson(MenuListData data) => json.encode(data.toJson());

class MenuListData {
    MenuListData({
        required this.ignore,
        required this.code,
        required this.message,
        required this.data,
    });

    int ignore;
    int code;
    String message;
    List<Datum> data;

    factory MenuListData.fromJson(Map<String, dynamic> json) => MenuListData(
        ignore: json["ignore"],
        code: json["code"],
        message: json["message"],
        data: List<Datum>.from(json["data"].map((x) => Datum.fromJson(x))),
    );

    Map<String, dynamic> toJson() => {
        "ignore": ignore,
        "code": code,
        "message": message,
        "data": List<dynamic>.from(data.map((x) => x.toJson())),
    };
}

class Datum {
    Datum({
        required this.ctgId,
        required this.name,
        required this.items,
    });

    String ctgId;
    String name;
    List<Item> items;

    factory Datum.fromJson(Map<String, dynamic> json) => Datum(
        ctgId: json["ctg_id"],
        name: json["name"],
        items: List<Item>.from(json["items"].map((x) => Item.fromJson(x))),
    );

    Map<String, dynamic> toJson() => {
        "ctg_id": ctgId,
        "name": name,
        "items": List<dynamic>.from(items.map((x) => x.toJson())),
    };
}

class Item {
    Item({
        required this.prodId,
        required this.prodName,
        required this.thumb,
    });

    String prodId;
    String prodName;
    String thumb;

    factory Item.fromJson(Map<String, dynamic> json) => Item(
        prodId: json["prod_id"],
        prodName: json["prod_name"],
        thumb: json["thumb"],
    );

    Map<String, dynamic> toJson() => {
        "prod_id": prodId,
        "prod_name": prodName,
        "thumb": thumb,
    };
}

Solution

  • You are using index for iteration of items as well, that is bad. Instead take first element from items.

    Your ListTile code should be like..

    return ListTile(
      leading: Image.network(
        snapshot.data!.data[index]!.items[0].thumb.toString() //Make sure items is not empty
      ),
      title: Text(snapshot.data!.data[index]!.items[0].prodName),
      subtitle: Text(snapshot.data!.data[index]!.name),
      trailing: Container(
        padding: EdgeInsets.all(5.r),
        decoration: BoxDecoration(
          color: Colors.transparent,
          borderRadius: BorderRadius.circular(10.r),
        ),
        child: Image.asset(
          "assets/images/MenuIcon.png", 
          height: 20.h, 
          width: 20.w,
        ),
      ),
    );
    

    Edited

    I added temporary fix with Column for iterating items in data.

    RefreshIndicator(
      onRefresh: () {
        setState(() {});
        return fetchOpenMenuList(widget.product);
      },
      child: FutureBuilder<MenuListData>(
        future: futureOpenMenuList,
        builder: (context, snapshot) {
          if (snapshot.hasData) {
            return ListView.builder(
              padding: EdgeInsets.symmetric(vertical: 8.h),
              itemCount: snapshot.data!.data.length,
              itemBuilder: (BuildContext context, int index) {
                return Column(
                  children: [
                    for(final item in snapshot.data!.data[index]!.items)
                    ListTile(
                      leading: Image.network(
                        item.thumb.toString()
                       ),
                       title: Text(item.prodName),
                       subtitle: Text(snapshot.data!.data[index]!.name),
                       trailing: Container(
                         padding: EdgeInsets.all(5.r),
                         decoration: BoxDecoration(
                           color: Colors.transparent,
                           borderRadius: BorderRadius.circular(10.r),
                         ),
                         child: Image.asset(
                           "assets/images/MenuIcon.png", 
                           height: 20.h, 
                           width: 20.w,
                         ),
                       ),
                     ),
                   ],
                 );
               },
             );
           } else if (snapshot.hasError) {
             return Center(child: Text('No Data Found'));
           }
           return const Center(
             child: SizedBox(
               height: 50.0,
               width: 50.0,
               child: CircularProgressIndicator(),
             ),
           );
         },
       ),
     )
    

    Hope this archives your goal.