Issue
So, I am working on a university major project with a teammate, me responsible with flutter and him with json & database, and I am struggling a bit here trying to print this json array from the API into flutter.
[
{
"semester": {
"semester_name": "2020/2021 Summer"
},
"offer": {
"course_id": "ECTE 421",
"section_no": 1,
"time": [
{
"day": "u",
"start_time": "13:00",
"end_time": "14:40"
},
{
"day": "m",
"start_time": "13:00",
"end_time": "14:40"
},
{
"day": "t",
"start_time": "13:00",
"end_time": "14:40"
},
{
"day": "w",
"start_time": "13:00",
"end_time": "14:40"
}
],
"instructor": {
"fname": "Ayman",
"sname": "Ahmed",
"tname": null,
"lname": "Alaiwi",
"info": {
"email": "[email protected]",
"role": "instructor",
"department": {
"dep_id": "ETE",
"dep_name": "Telecommunications Engineering Department",
"college": {
"college_id": "ENG",
"college_name": "COLLEGE OF ENGINEERING"
}
}
}
},
"room": {
"type": "Lab",
"room_id": 5
}
}
},
{
"semester": {
"semester_name": "2020/2021 Summer"
},
"offer": {
"course_id": "IERM 498",
"section_no": 1,
"time": [
{
"day": "u",
"start_time": "15:00",
"end_time": "16:40"
},
{
"day": "t",
"start_time": "15:00",
"end_time": "16:40"
}
],
"instructor": {
"fname": "Hasan",
"sname": "Ali",
"tname": null,
"lname": "Razzaqi",
"info": {
"email": "[email protected]",
"role": "instructor",
"department": {
"dep_id": "ITMS",
"dep_name": "Multimedia Science Department",
"college": {
"college_id": "IT",
"college_name": "COLLEGE OF INFORMATION AND TECHNOLOGY"
}
}
}
},
"room": {
"type": "Hall",
"room_id": 9
}
}
},
{
"semester": {
"semester_name": "2020/2021 Summer"
},
"offer": {
"course_id": "INTR 463",
"section_no": 1,
"time": [],
"instructor": {
"fname": "Suresh",
"sname": null,
"tname": null,
"lname": "Subramanian",
"info": {
"email": "[email protected]",
"role": "chair",
"department": {
"dep_id": "ITMS",
"dep_name": "Multimedia Science Department",
"college": {
"college_id": "IT",
"college_name": "COLLEGE OF INFORMATION AND TECHNOLOGY"
}
}
}
},
"room": {
"type": "Lab",
"room_id": 1
}
}
}
]
I keep getting a 'RangeError (index): Invalid value: Valid value range is empty: 0' and a 'RangeError (index): Invalid value: Not in inclusive range 0..1: 2' . So immediately I knew it had something to do with me going out of bound when it comes to the second array and the third one being empty (but i worked around the third one by making the errors transparent since it doesn't hold any important info).
So is there a way to go around this in flutter? Cause I cant find any sources online with these kind of arrays.
Here's my flutter code for the page:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.red,
),
debugShowCheckedModeBanner: false,
home: schedule());
}
}
class schedule extends StatefulWidget {
@override
printing createState() => printing();
}
class Schedule {
final Map<String, dynamic> semester;
final Map<String, dynamic> offer;
Schedule(this.semester, this.offer);
}
Future getschedule() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
String? token = prefs.getString('access_token');
final response = await http
.get(Uri.parse('http://3.6.24.16:5555/student/schedule'), headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Authorization': 'Bearer $token',
});
int counter=0;
var jsonData = jsonDecode(response.body);
List<Schedule> schedules = [];
if (response.statusCode == 200) {
for (var u in jsonData) {
Schedule schedule = Schedule(u["semester"], u["offer"]);
schedules.add(schedule);
counter++;
}
print(schedules);
return schedules;
} else {
throw Exception('Failed to load');
}
}
class printing extends State<schedule> {
int counter=0;
static const keyLanguage = 'key-language';
static const keyDarkMode = 'key-dark-mode';
@override
void initState() {
getschedule();
super.initState();
}
dynamic removeNull(dynamic params) {
if (params is Map) {
var _map = {};
params.forEach((key, value) {
var _value = removeNull(value);
if (_value != null) {
_map[key] = _value;
}
});}}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.red,
centerTitle: true,
title: Image.asset("assets/images/logo20B.png",
fit: BoxFit.contain,
height: 130,
width: 130,
alignment: Alignment.center),
),
body: ListView(
children: [
Column(
children: [
FutureBuilder(
future: getschedule(),
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: 1,
itemBuilder: (BuildContext context, index) {
return Column(children: [
Padding(
padding: EdgeInsets.all(16.0),
child: Center(
child: Text(
snapshot
.data[index].semester["semester_name"]
.toString(),
style: TextStyle(
fontSize: 30,
color: Colors.black,
),
))),
Table(
children: [
TableRow(children: [
Center(child: Text("Course")),
Center(child: Text("Sec")),
Center(child: Text("Day")),
Center(child: Text("Time")),
Center(child: Text("Room"))
])
],
)
]);
},
);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
return const CircularProgressIndicator();
}),
FutureBuilder(
future: getschedule(),
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, index) {
return Card(
child: Table(
border: TableBorder.all(color: Colors.black),
children: [
TableRow(children: [
Center(
child: Text(snapshot
.data[index].offer["course_id"]
.toString())),
Center(
child: Text(snapshot
.data[index].offer["section_no"]
.toString())),
Center(
child: Text(snapshot
.data[index].offer["time"][0]["day"]
.toString())),
Center(
child: Text(snapshot.data[index]
.offer["time"][0]["start_time"]
.toString() +
" to " +
snapshot.data[index]
.offer["time"][0]["end_time"]
.toString())),
Center(
child: Text(snapshot
.data[index].offer["room"]["type"]
.toString() +
" " +
snapshot.data[index]
.offer["room"]["room_id"]
.toString())),
]),
TableRow(children: [
Center(
child: Text("")),
Center(
child: Text("")),
Center(
child: Text(snapshot
.data[index].offer["time"][1]["day"]
.toString())),
Center(
child: Text(snapshot.data[index]
.offer["time"][1]["start_time"]
.toString() +
" to " +
snapshot.data[index]
.offer["time"][1]["end_time"]
.toString())),
Center(
child: Text(snapshot
.data[index].offer["room"]["type"]
.toString() +
" " +
snapshot.data[index]
.offer["room"]["room_id"]
.toString())),
]),
],
));
},
);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
return const CircularProgressIndicator();
}),
],
)
],
));
}
}
Any advice/help/criticism will be appreciated, I am here to learn!
UPDATE: So, I didn't find an exact solution, but got inspired by a response I got and decided to go with it due to time constraints. I want to share it with anyone who might struggle with a similar problem.
FutureBuilder(
future: getschedule(),
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: 1,
itemBuilder: (BuildContext context, index) {
return Column(children: [
Padding(
padding: EdgeInsets.all(16.0),
child: Center(
child: Text(
snapshot.data[index]
.semester["semester_name"]
.toString(),
style: TextStyle(
fontSize: 30,
color: Colors.black,
),
))),
Table(
children: [
TableRow(children: [
Center(child: Text("Course")),
Center(child: Text("Sec")),
Center(child: Text("Day")),
Center(child: Text("Time")),
Center(child: Text("Room"))
])
],
)
]);
});
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
return const CircularProgressIndicator();
}),
FutureBuilder(
future: getschedule(),
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: snapshot.data.length-1,
itemBuilder: (BuildContext context, index) {
return Card(
child: Table(
border: TableBorder.all(color: Colors.black),
children: [
TableRow(children: [
Center(
child: Text(snapshot
.data[index].offer["course_id"]
.toString())),
Center(
child: Text(snapshot
.data[index].offer["section_no"]
.toString())),
Center(
child: Text(snapshot
.data[index].offer["time"][0]["day"]
.toString())),
Center(
child: Text(snapshot.data[index]
.offer["time"][0]["start_time"]
.toString() +
" to " +
snapshot.data[index]
.offer["time"][0]["end_time"]
.toString())),
Center(
child: Text(snapshot.data[index]
.offer["room"]["type"]
.toString() +
" " +
snapshot.data[index]
.offer["room"]["room_id"]
.toString())),
]),
],
));
},
);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
return const CircularProgressIndicator();
}),
FutureBuilder(
future: getschedule(),
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: snapshot.data.length-1,
itemBuilder: (BuildContext context, index) {
return Card(
child: Table(
border: TableBorder.all(color: Colors.black),
children: [
TableRow(children: [
Center(
child: Text(snapshot
.data[index].offer["course_id"]
.toString())),
Center(
child: Text(snapshot
.data[index].offer["section_no"]
.toString())),
Center(
child: Text(snapshot
.data[index].offer["time"][1]["day"]
.toString())),
Center(
child: Text(snapshot.data[index]
.offer["time"][1]["start_time"]
.toString() +
" to " +
snapshot.data[index]
.offer["time"][1]["end_time"]
.toString())),
Center(
child: Text(snapshot.data[index]
.offer["room"]["type"]
.toString() +
" " +
snapshot.data[index]
.offer["room"]["room_id"]
.toString())),
]),
],
));
},
);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
return const CircularProgressIndicator();
}),
FutureBuilder(
future: getschedule(),
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: snapshot.data.length-1,
itemBuilder: (BuildContext context, index) {
return Card(
child: Table(
border: TableBorder.all(color: Colors.black),
children: [
TableRow(children: [
Center(
child: Text(snapshot
.data[index].offer["course_id"]
.toString())),
Center(
child: Text(snapshot
.data[index].offer["section_no"]
.toString())),
Center(
child: Text(snapshot
.data[index].offer["time"][2]["day"]
.toString())),
Center(
child: Text(snapshot.data[index]
.offer["time"][2]["start_time"]
.toString() +
" to " +
snapshot.data[index]
.offer["time"][2]["end_time"]
.toString())),
Center(
child: Text(snapshot.data[index]
.offer["room"]["type"]
.toString() +
" " +
snapshot.data[index]
.offer["room"]["room_id"]
.toString())),
]),
],
));
},
);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
return const CircularProgressIndicator();
}),
FutureBuilder(
future: getschedule(),
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: snapshot.data.length-1,
itemBuilder: (BuildContext context, index) {
return Card(
child: Table(
border: TableBorder.all(color: Colors.black),
children: [
TableRow(children: [
Center(
child: Text(snapshot
.data[index].offer["course_id"]
.toString())),
Center(
child: Text(snapshot
.data[index].offer["section_no"]
.toString())),
Center(
child: Text(snapshot
.data[index].offer["time"][3]["day"]
.toString())),
Center(
child: Text(snapshot.data[index]
.offer["time"][3]["start_time"]
.toString() +
" to " +
snapshot.data[index]
.offer["time"][3]["end_time"]
.toString())),
Center(
child: Text(snapshot.data[index]
.offer["room"]["type"]
.toString() +
" " +
snapshot.data[index]
.offer["room"]["room_id"]
.toString())),
]),
],
));
},
);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
return const CircularProgressIndicator();
}),
So, what i ended up doing was first, changing the range of the itemcount for all listview builders to the length of the list-1, cause it wouldn't print any other way. And then I made multiple futurebuilder for every working day of the week, sunday [0] to thursday [4].
Hope it makes sense and helps you.
Solution
For 'RangeError (index): Invalid value: Valid value range is empty: 0' and a 'RangeError (index): Invalid value: Not in inclusive range 0..1: 2'
, add the not null & length check before trying to access the array.
For ex: snapshot.data[index].offer["time"][0]["day"]
To check the time
is not null and there's an element at 0th index in above line, we can add checks like this:
if(snapshot.data[index].offer["time"] != null &&
snapshot.data[index].offer["time"][0] != null)
Center(
Text(snapshot.data[index].offer["time"][0]["day"])
),
You can also use the ternary operator alternatively
Additionally, I would say to implement the Dart class for the JSON data you receive through API (not mandatory), but it'll optimize the length checking or error handling operations while presenting data to the user.
Converting JSON into class Objects in dart
JSON to Dart Converter
Answered By - Pathik Patel
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.