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,
};
}
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.