I'm trying to access my JSON data, but it returns me this error. I already manipulated the structure of my JSON but nothing seems to fix. I tried to use [{:[{}]}]
or {:[{}]}
for JSON format but the error persist. I want to display the data that I can get into a container.
ERROR:
RangeError (RangeError (index): Invalid value: Valid value range is empty: 0)
This is my code.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/flutter_svg.dart';
import '../constants (2).dart';
import '../constants.dart';
import '../size_config.dart';
class BreakfastCard extends StatefulWidget {
BreakfastCard({
Key? key,
this.width = 140,
this.aspectRetio = 1.02,
}) : super(key: key);
final double width, aspectRetio;
@override
_BreakfastCardState createState() => _BreakfastCardState();
}
class _BreakfastCardState extends State<BreakfastCard> {
List breakfast = [];
Future<void> loadBreakfastAsset() async {
final String loadBreakfastAsset = await rootBundle.loadString('assets/data.json');
final breakfast = await json.decode(loadBreakfastAsset);
}
@override
Widget build(BuildContext context) {
SizeConfig().init(context);
return Padding(
padding: EdgeInsets.only(left: getProportionateScreenWidth(20)),
child: SizedBox(
width: getProportionateScreenWidth(140),
child: GestureDetector(
onTap: (){},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AspectRatio(
aspectRatio: 1.02,
child: Container(
padding: EdgeInsets.all(getProportionateScreenWidth(20)),
decoration: BoxDecoration(
color: kSecondaryColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(15),
),
child: Hero(
tag: breakfast[0]["id"],
child: Image.asset(breakfast[0]["images"]),
),
),
),
const SizedBox(height: 10),
Text(
breakfast[0]["title"],
style: const TextStyle(color: Colors.black),
maxLines: 2,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"${breakfast[0]["calories"]} cal |",
style: TextStyle(
fontSize: getProportionateScreenWidth(18),
fontWeight: FontWeight.bold,
color: kPrimaryColor,
),
),
Text(
"${breakfast[0]["time"]} min",
style: TextStyle(
fontSize: getProportionateScreenWidth(18),
fontWeight: FontWeight.w600,
color: kPrimaryColor,
),
),
InkWell(
borderRadius: BorderRadius.circular(50),
onTap: () { breakfast[0]["isFavorite"] = !breakfast[0]["isFavorite"];},
child: Container(
padding: EdgeInsets.all(getProportionateScreenWidth(8)),
height: getProportionateScreenWidth(28),
width: getProportionateScreenWidth(28),
child: SvgPicture.asset(
"assets/icons/Heart Icon_2.svg",
color: breakfast[0]["images"]
? const Color(0xFFFF4848)
: const Color(0xFFDBDEE4),
),
),
),
],
)
],
),
),
),
);
}
}
Here is my json file :
[{
"items": [{
"id": "1",
"rating": "0.0",
"images": [
"assets/images/cilantro.png"
],
"title": "Cilantro and Kale Pesto Toast with a Fried Egg",
"time": "15",
"description": "Sliced bread is the perfect blank canvas, ready to be loaded up with virtuous ingredients.",
" rating": "4.8",
"isFavorite": "false",
"isPopular": "true",
"calories": "405",
"serving": 1,
"naturalFacts": [
"405 calories",
"protein 15g",
"fat 31g",
"saturated fat 5.8g",
"carbohydrates 16g",
"fiber 1.9g",
"sodium 331mg",
"cholesterol 189mg"
],
"ingredients": [
"¼ cup packed cilantro",
"1 cup packed kale leaves",
"¼ cup extra-virgin olive oil",
"1 tablespoon white balsamic vinegar",
"2 tablespoons hulled hemp seeds*",
"salt",
"Freshly ground pepper",
"1 large slice of whole-wheat toast",
"2 tablespoons unflavored whole-milk Greek yogurt",
"1 fried egg"
],
"procedure": [
"Whirl the cilantro, kale leaves, extra-virgin olive oil, white balsamic vinegar, and hemp seeds* until fairly smooth, scraping inside of bowl.",
"Season with sea salt and freshly ground pepper. Smear a large slice of whole-wheat toast with the yogurt, then with some pesto.",
"Top with a fried egg and more salt and pepper."
]
}]
}]
Here is the Models
import 'package:flutter/material.dart';
class Breakfast {
final int id, time, serving;
final String title, description, calories;
final List <String> procedure;
final List <String> ingredients;
final List <String> naturalFacts;
final List<String> images;
final double rating;
bool isFavorite, isPopular;
Breakfast({
required this.id,
required this.images,
this.rating = 0.0,
this.isFavorite = false,
this.isPopular = false,
required this.title,
required this.time,
required this.description,
required this.ingredients,
required this.procedure,
required this.naturalFacts,
required this.calories,
required this.serving,
});
factory Breakfast.fromJson(Map<String, dynamic> parsedJson) {
var procedureFromJson = parsedJson['procedure'];
var ingredientsFromJson = parsedJson['ingredients'];
var naturalFactsFromJson = parsedJson['naturalFacts'];
var imagesFromJson = parsedJson['images'];
//print(streetsFromJson.runtimeType);
// List<String> streetsList = new List<String>.from(streetsFromJson);
List<String> ingredientsList = ingredientsFromJson.cast<String>();
List<String> procedureList = procedureFromJson.cast<String>();
List<String> imagesList = imagesFromJson.cast<String>();
return new Breakfast(
calories: parsedJson['calories'],
time: parsedJson['time'],
title: parsedJson['title'],
description: parsedJson['description'],
naturalFacts: parsedJson['naturalFacts'],
serving: parsedJson['serving'],
id: parsedJson['id'],
procedure: procedureList,
ingredients: ingredientsList,
images: imagesList,
);
}
}
CodePudding user response:
Firstly, while we don't need []
at outer side of JSON
data.
The second issue is coming from data type. On JSON modeling. If you look closely, id
is String on JSON but int
on model class. Convert id
to int on JSON,
Like "id": 1,
also same goes for others fields, do a check on datatype and create model based on that.
Thirdly, it requires using FutureBuilder
to load asset's JSON and parsing it.
It will be like
Future<List<Breakfast>> loadBreakfastAsset() async {
final String _loadBreakfastAsset =
await rootBundle.loadString('json/bg.json');
final jsonString = await json.decode(_loadBreakfastAsset)["items"] as List;
final breakfastList = jsonString.map((e) => Breakfast.fromJson(e)).toList();
return breakfastList;
}
You follow my note on it.
I am sharing the tested code-snippet, you need to check and apply to all.
class Breakfast {
final int id;
final int time;
final int serving;
final String title;
final List<String> procedure;
Breakfast({
required this.id,
required this.time,
required this.serving,
required this.title,
required this.procedure,
});
factory Breakfast.fromJson(Map<String, dynamic> parsedJson) {
var procedureFromJson = parsedJson['procedure'];
List<String> procedureList = procedureFromJson.cast<String>();
return Breakfast(
title: parsedJson['title'],
serving: parsedJson['serving'],
id: parsedJson['id'],
time: parsedJson['time'],
procedure: procedureList,
);
}
}
class _BreakfastCardState extends State<BreakfastCard> {
Future<List<Breakfast>> loadBreakfastAsset() async {
final String _loadBreakfastAsset =
await rootBundle.loadString('json/bg.json');
final jsonString = await json.decode(_loadBreakfastAsset)["items"] as List;
final breakfastList = jsonString.map((e) => Breakfast.fromJson(e)).toList();
return breakfastList;
}
@override
Widget build(BuildContext context) {
// SizeConfig().init(context);
return FutureBuilder<List<Breakfast>>(
future: loadBreakfastAsset(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
} else if (snapshot.hasError) {
return Text("Got ERR :${snapshot.error}");
} else if (snapshot.hasData) {
final List<Breakfast> breakfast = snapshot.data!;
return Padding(
padding: const EdgeInsets.only(left: (20)),
child: SizedBox(
// width: getProportionateScreenWidth(140),
width: 100,
height: 200,
child: GestureDetector(
onTap: () {},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AspectRatio(
aspectRatio: 1.02,
child: Container(
// padding: EdgeInsets.all(getProportionateScreenWidth(20)),
decoration: BoxDecoration(
// color: kSecondaryColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(15),
),
// child: Hero(
// tag: breakfast[0]["id"],
// child: Image.asset(breakfast[0]["images"]),
// ),
),
),
const SizedBox(height: 10),
Text(
breakfast[0].title,
style: const TextStyle(color: Colors.black),
maxLines: 2,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
// Text(
// "${breakfast[0].calories} cal |",
// style: TextStyle(
// // fontSize: getProportionateScreenWidth(18),
// fontWeight: FontWeight.bold,
// // color: kPrimaryColor,
// ),
// ),
Text(
"${breakfast[0].time} min",
style: TextStyle(
// fontSize: getProportionateScreenWidth(18),
fontWeight: FontWeight.w600,
// color: kPrimaryColor,
),
),
InkWell(
borderRadius: BorderRadius.circular(50),
onTap: () {
// breakfast[0].isFavorite =
// !breakfast[0].isFavorite;
},
child: Container(
padding: EdgeInsets.all((8)), child: Text("A")),
),
],
)
],
),
),
),
);
}
return Text("...");
});
}
}
CodePudding user response:
it happens when you are calling a list which doesn't have enough length according to the index you passed i.e. breakfast[0]
you have made a Future but you need to call it using FutureBuilder so thar data can be passed into your list and can be accessed here breakfast[0]