0
votes

I'm trying to use data from the API, but the data I need is found for different requests. In flutter there is future.waite and with this I can make a request for the necessary parts of the API. The idea is that I am trying to create schedule for the train. In this train schedule, I need train number, platform name and arrival time. I'm trying to do everything as described in the documentation and on the video, but I can't do it, because I get an error 'The method '[]' was called on null. Receiver: null Tried calling: []("st_title")'

Before asking here I tried to make something like this:

body:ListView.builder(itemBuilder: (context, index){
          return ListTile(
            title: Text(stops[index]['st_title']),

but it dosen't work and gives me an error:

The method '[]' was called on null. Receiver: null Tried calling:

I saw a solution to a similar error here, but I've tried all the solutions and can't figure out what exactly I'm doing wrong. Could you please point me out? May be I don't actually realize the concepts of what exactly I should do. Can you, guys, help me?

My full code is:

Map <String, dynamic> stops;
  Map <String, dynamic> marhe;
 Map <String, dynamic> arrival;
 
@override
  void initState() {
    // TODO: implement initState
    super.initState();

    fetchData();
  }

 Future fetchData() async{
    String username = '***';
    String password = '***';
    String basicAuth =
        'Basic ' + base64Encode(utf8.encode('$username:$password'));
    print(basicAuth);
  final result = await Future.wait([
    http.get( 'http://link/getTableCur.php?fmt=json',
        headers: <String, String>{'authorization': basicAuth}),
    http.get( 'http://link//getStops.php?fmt=json',
        headers: <String, String>{'authorization': basicAuth}),
    http.get( 'http://link//getMarshes.php?fmt=json',
        headers: <String, String>{'authorization': basicAuth}),
  ]);
  setState(() {
    stops = json.decode(result[0].body);
    marhe = json.decode(result[1].body);
    arrival = json.decode(result[2].body);
  });
  }


  @override


  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
        ),
        body:ListView.builder(itemBuilder: (context, index){
          return ListTile(
            title: Text(stops[index]['st_title']),
            leading: Text(marhe['mr_num']),
            subtitle: Text(arrival['ta_arrivetime'].toString()),
          );
        }
            //title: Text(arrival[index]['tc_arrivetime']?? ''),
          ),

UPDATE!

Thanks for helping me @Patrick Freitas, I was trying to make everything he said but now I get an error:

The getter 'length' was called on null.
Receiver: null
Tried calling: length

for the following code:

 body:  stops.length > 0 ? ListView.builder(
              //itemCount: stops.length,
              itemBuilder: (BuildContext context, int index) {
                return ListTile(
                 trailing: Text(marhe[index]["mr_num"]),
                  title: Text(stops[index]['st_title']),
                 // subtitle: Text(arrival[index]["ta_arrivetime"]),
                );
              },
            ) : CircularProgressIndicator()
        )

also when I use isDataObtained ? ListView.builder{...}:CircularProgressIndicator()

then it gives me endless loading and no data appears on the screen. It also gives an error "Error: List<dynamic> is not a subtype of type Map<String, dynamic>" also I have found the similiar problem solving on this question - Error: List<dynamic> is not a subtype of type Map<String, dynamic>

and my code looks like this:

 setState(() {

      stops = json.decode(result[0].body[0]);
      marhe = json.decode(result[1].body[0]);
      arrival = json.decode(result[2].body[0]);
      isDataObtained = true;
    });
  }

Also my json's for all of this links looks like this:

for **getTableAll**
[
  {
    "ta_id": 1,
    "srv_id": 1,
    "st_id": 3026,
    "mr_id": 305,
    "rl_racetype": "B",
    "uniqueid": 21,
    "ta_systime": "2021-03-11 15:01:47",
    "ta_arrivetime": "2021-03-11 15:06:11",
    "mv_id": 957,
    "rc_kkp": "",
    "ta_len2target": 4.996839,
    "u_inv": false
  },
  {
for **getStops**
{
    "st_id": 1,
    "ok_id": "18410",
    "sr_id": 0,
    "st_title": "Station1",
    "st_desc": "Station1",
  },

for **Marshes:** 
[
  {
    "mr_id": 1,
    "tt_id": 1,
    "mt_id": 2,
    "mr_num": "112",
  
  },

These are three arrays and I need data from all three arrays at the same time: as I described above, I need data on arrival time, train number and platform name. I tried to use classic data retrieval as described in the flutter documentation, but nothing worked for me. Here on Stackoverflow, I was told that I need to use Future.wait, but now I stuck and got confused.

1

1 Answers

0
votes

Flutter doesn't know if your async request already returned, so it is trying to render a array without any information. To prevent that, you can add a IF validation to verify if your array it's already populated.

Using a ternary if:

body:
   stops.length > 0 ? ​
      ListView.builder(...) :
      CircularProgressIndicator() 

As you have three arrays to be populated, you can create a boolean to control if your request already populated all three, like isDataObtained.

Map <String, dynamic> stops;
Map <String, dynamic> marhe;
Map <String, dynamic> arrival;
bool isDataObtained = false;


Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
            appBar: AppBar(),
            body:
                isDataObtained ? ​
                    ListView.builder(...) :
                    CircularProgressIndicator() 
        )
    )
}


Future fetchData() async{
    final result = await Future.wait([...]);
    setState(() {
        stops = json.decode(result[0].body);
        marhe = json.decode(result[1].body);
        arrival = json.decode(result[2].body);
        isDataObtained = true;
    });
}