I have fetched the details of the planet i.e. (NOM) No. of moons, Gravity and Density from an API. And i have stored these details in an array PlanetInfo. And i want to display it using text widget like Text("${PlanetInfo[1]}", style: TextStyle(color: Colors.white)). but it is giving me an erro: RangeError (index): Invalid value: Valid value range is empty: 1
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'package:xperience/Models/planets.dart';
class PlanetDescNoMGD extends StatelessWidget {
final Planet planeteee;
List PlanetInfo = [];
getPlanetData() async {
var url =
"https://api.le-systeme-solaire.net/rest/bodies/${planeteee.planetApi}";
final uri = Uri.parse(url);
final response = await http.get(uri);
final body = response.body;
final jsondata = jsonDecode(body);
PlanetInfo.add(jsondata["moons"].length);
PlanetInfo.add(jsondata["gravity"]);
PlanetInfo.add(jsondata["density"]);
}
PlanetDescNoMGD({Key? key, required this.planeteee}) : super(key: key);
@override
void initState() {
this.getPlanetData();
}
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
const Text(
"No. of moons",
style:
TextStyle(color: Colors.white, fontWeight: FontWeight.w700),
),
const SizedBox(
height: 12,
),
Text("${PlanetInfo[1]}", style: TextStyle(color: Colors.white))
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
const Text(
"Gravity",
style:
TextStyle(color: Colors.white, fontWeight: FontWeight.w600),
),
const SizedBox(
height: 12,
),
Text("${PlanetInfo[1]}" " m/s²",
style: TextStyle(color: Colors.white))
//Text(${num} " m/s²", style: TextStyle(color: Colors.white))
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
const Text(
"Density",
style:
TextStyle(color: Colors.white, fontWeight: FontWeight.w600),
),
const SizedBox(
height: 12,
),
Text("${PlanetInfo[2]}" " g/cm3",
style: TextStyle(color: Colors.white))
],
),
],
);
}
}
I need a solution.
CodePudding user response:
You do not know how to work with async functions. Please learn it. You are making async request to API and directly trying to use respond from API. It is not possible. What is happening is your List PlanetInfo is empty when build is called. so you are getting error.
Stateless widgets does not have initState! I see you are trying to override initState that does not exist.
You need stateful widget to use setState.
Here is, with minimal change working version of your code. You need to use setState after data is loaded and change isLoaded = true.
Here is solution:
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'package:xperience/Models/planets.dart';
class PlanetDescNoMGD extends StatefulWidget {
PlanetDescNoMGD({Key? key, required this.planeteee}) : super(key: key);
final Planet planeteee;
@override
State<PlanetDescNoMGD> createState() => _PlanetDescNoMGDState();
}
class _PlanetDescNoMGDState extends State<PlanetDescNoMGD> {
List PlanetInfo = [];
bool isLoaded = false;
getPlanetData() async {
var url =
"https://api.le-systeme-solaire.net/rest/bodies/${widget.planeteee.planetApi}";
final uri = Uri.parse(url);
final response = await http.get(uri);
final body = response.body;
final jsondata = jsonDecode(body);
PlanetInfo.add(jsondata["moons"].length);
PlanetInfo.add(jsondata["gravity"]);
PlanetInfo.add(jsondata["density"]);
setState(() {
isLoaded = true;
});
}
@override
void initState() {
super.initState();
getPlanetData();
}
@override
Widget build(BuildContext context) {
return !isLoaded
? const Center(child: CircularProgressIndicator())
: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
const Text(
"No. of moons",
style: TextStyle(
color: Colors.white, fontWeight: FontWeight.w700),
),
const SizedBox(
height: 12,
),
Text("${PlanetInfo[1]}",
style: TextStyle(color: Colors.white))
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
const Text(
"Gravity",
style: TextStyle(
color: Colors.white, fontWeight: FontWeight.w600),
),
const SizedBox(
height: 12,
),
Text("${PlanetInfo[1]}" " m/s²",
style: TextStyle(color: Colors.white))
//Text(${num} " m/s²", style: TextStyle(color: Colors.white))
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
const Text(
"Density",
style: TextStyle(
color: Colors.white, fontWeight: FontWeight.w600),
),
const SizedBox(
height: 12,
),
Text("${PlanetInfo[2]}" " g/cm3",
style: TextStyle(color: Colors.white))
],
),
],
);
}
}
CodePudding user response:
You have got 2 things to fix there.
- Class.
Do not use PlanetInfo as a list of ints. Make full use of defining a class: encapsulating data and improving readability.
Define the class as follows
PlanetInfo{
int noOfMoons;
double gravity;
double density;
PlanetInfo({
required this.noOfMoons,
required this.gravity,
required this.density,
});
}
Declare a member in state.
List PlanetInfo = [];//remove this
late final PlanetInfo myPlanet;// use this
Now create an object in your fetch call and assign it to state member.
final jsondata = jsonDecode(body);
PlanetInfo tempPlanet=PlanetInfo(
noOfMoons: jsondata["moons"].length,
gravity: jsondata["gravity"],
density: jsondata["density"],
);
myPlanet=x; // use setState() if required
and display in widgets using attributes.
//rest of code
Text("${planetInfo.noOfMoons}",
//rest of code
Text("${planetInfo.gravity}}" " m/s²",'
//rest of code
Text("${planetInfo.density}" " g/cm3",
//rest of code
- Displaying API data.
You can either use FutureBuilder to wait on building UI until api call is done, or do as @hiloliddin suggested (using isLoaded member and building ui based on its value).
In your current implementation you were getting the error because UI was built before api call finished and the list PlanetInfo was empty.