Asyraf Dayan
Asyraf Dayan

Reputation: 3071

Flutter method [] was called on null

I made a random json that has a list of people and things they bought and this is the content:

random.json

{
  "people": [
    {
      "name": "person1",
      "id": "1",
      "thingsbought": {
        "fish": {
          "price" : "10"
        },
        "chicken": {
          "price" : "5"
        },
        "vegetables": {
          "price" : "15"
        },
        "drinks": {
          "price" : "10"
        }
      }
    },
    {
      "name": "person2",
      "id": "2",
      "thingsbought": {
        "fish": {
          "price" : "10"
        },
        "vegetables": {
          "price" : "15"
        }
      }
    },    
    {
      "name": "person3",
      "id": "3",
      "thingsbought": {
        "chicken": {
          "price" : "5"
        },
        "vegetables": {
          "price" : "15"
        },
        "drinks": {
          "price" : "10"
        }
      }
    }
  ]
}

The app consists of two pages. The first page displays the list of names with their ID's while the next page lists things they bought based on their index number in the JSON.

I dont have a problem with the first page. This is the screenshot for context purposes.

First Page First Page

While this is the next page after clicking on any person

ThingsBought.dart (Second Page)

import 'package:flutter/material.dart';
import 'dart:async' show Future;
import 'dart:convert';
import 'package:http/http.dart' as http;

final String url = "http://crm.emastpa.com.my/random.json";

Future<String> loadThings() async {
  var res = await http.get(
      Uri.encodeFull(url),
      headers: {"Accept": "application/json"});
  return res.body;
}

class ThingsBought extends StatefulWidget {

  ThingsBought({Key key, this.index, this.name}) : super(key:key);

  final int index;
  final String name;

  @override
  _ThingsBoughtState createState() => _ThingsBoughtState();
}

class _ThingsBoughtState extends State<ThingsBought> {

  makeCard(String title, String price){
    return new Card(
      child: new ExpansionTile(
          title: new Text(title),
          children: <Widget>[
            new ListTile(
              title: new Text("Price"),
              trailing: new Text(price),
            )
          ]),
    );
  }

  @override
  Widget build(BuildContext context) {

    Widget body = new Container(
      child: new FutureBuilder(
          future: loadThings(),
          builder: (context, snapshot){
            if(snapshot.hasData){

              List<Widget> widgets = [];

              Map decoded = json.decode(snapshot.data)["people"][widget.index]["thingsbought"];


              var fishprice = decoded["fish"]["price"];


              var chickenprice = decoded["chicken"]["price"];


              var vegetableprice = decoded["vegetables"]["price"];


              var drinkprice = decoded["drinks"]["price"];


              if(decoded.containsKey("fish")){
                widgets.add(makeCard("Fish", fishprice));
              }else{
                return new Container();
              }

              if(decoded.containsKey("chicken")){
                widgets.add(makeCard("Chicken", chickenprice));
              }else{
                return new Container();
              }

              if(decoded.containsKey("vegetables")){
                widgets.add(makeCard("Vegetables", vegetableprice));
              }else{
                return new Container();
              }

              if(decoded.containsKey("drinks")){
                widgets.add(makeCard("Drinks", drinkprice));
              }else{
                return new Container();
              }


              return new Column(
                children: widgets,
              );
            }else{
              return new Center(
                child: new CircularProgressIndicator(),
              );
            }
          }
      ),
    );

    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Things Bought"),
      ),
      body: new SingleChildScrollView(
        child: body,
      ),
    );
  }
}

The issue here is that, whenever I click on the person1 which is the first index, It comes out just fine.

Screenshot for first index that works just fine

FIRST INDEX

But clicking on the other persons results in an error as shown in the screenshot below.

Screenshot for second index and third index which has the error

Things Bought

I know for sure that the first index works because all of the keys exist. But I wonder why the other ones failed to be read even when I put return new Container(); as the else statement.

Could anyone explain to me what I am doing wrong and how do I correct my code?

Upvotes: 1

Views: 8889

Answers (2)

Asyraf Dayan
Asyraf Dayan

Reputation: 3071

For anyone facing this issue, I have found the solution that worked for me.

The problem occurs when the variables needed to call the Keys are declared outside the if statement

THIS IS WRONG

          var fishprice = decoded["fish"]["price"];


          var chickenprice = decoded["chicken"]["price"];


          var vegetableprice = decoded["vegetables"]["price"];


          var drinkprice = decoded["drinks"]["price"];


          if(decoded.containsKey("fish")){
            widgets.add(makeCard("Fish", fishprice));
          }else{
            return new Container();
          }

          if(decoded.containsKey("chicken")){
            widgets.add(makeCard("Chicken", chickenprice));
          }else{
            return new Container();
          }

          if(decoded.containsKey("vegetables")){
            widgets.add(makeCard("Vegetables", vegetableprice));
          }else{
            return new Container();
          }

          if(decoded.containsKey("drinks")){
            widgets.add(makeCard("Drinks", drinkprice));
          }else{
            return new Container();
          }

THIS IS THE CORRECTED VERSION

              //Declare the variables inside the if statement and remove the return from new Container()
              if(decoded.containsKey("fish")){
                var fishprice = decoded["fish"]["price"];
                widgets.add(makeCard("Fish", fishprice));
              }else{
                new Container();
              }

              if(decoded.containsKey("chicken")){
                var chickenprice = decoded["chicken"]["price"];
                widgets.add(makeCard("Chicken", chickenprice));
              }else{
                new Container();
              }

              if(decoded.containsKey("vegetables")){
                var vegetableprice = decoded["vegetables"]["price"];
                widgets.add(makeCard("Vegetables", vegetableprice));
              }else{
                new Container();
              }

              if(decoded.containsKey("drinks")){
                var drinkprice = decoded["drinks"]["price"];
                widgets.add(makeCard("Drinks", drinkprice));
              }else{
                new Container();
              }

So the reasoning behind this change is that: declaring the variables outside the if statement will already call from the json and that is why the error occurs. Therefore the correct way to do it is to call if after the if statement confirms the existence of the key.

Upvotes: 1

Hadrien Lejard
Hadrien Lejard

Reputation: 5924

In your JSON, fish, chicken, vegetables, drinks are not set. The following code will not work. decoded["fish"] could be null.

var fishprice = decoded["fish"]["price"];
var chickenprice = decoded["chicken"]["price"];
var vegetableprice = decoded["vegetables"]["price"];
var drinkprice = decoded["drinks"]["price"];

Upvotes: 1

Related Questions