0

snapshot.hasData is returning null and the drop button never gets displayed.

I am trying to fetch data from the api and display it in a dropdown button. the response.body is fetched correctly as it gets printed when i try to print it. But when I try to print listOfOperators, nothing is printed. Looks like the listOfOperators is empty, hence the snapshot doesnt have any data. I am not able to figure out why this is the case

JsonData:

{"status": "success", "OperatorType": "GAS", "data": {"MAHANAGAR_GAS_LTD": {"name": "Mahanagar Gas Limited", "authenticator3": null, "label": "Customer Account Number (12 digits start with 21)", "note": null, "account_label": "Bill Group Number (1 - 8 characters + digits)", "authenticator3_options": null}, "TRIPURA_NATURAL_GAS_COMPANY_LTD": {"name": "Tripura Natural Gas Company Ltd", "authenticator3": null, "label": "Consumer Number (1 - 20 digits)", "note": null, "account_label": null, "authenticator3_options": null}, "HARAYANA_CITY_GAS": {"name": "Haryana City gas", "authenticator3": null, "label": "CRN Number (8-12 digits)", "note": null, "account_label": null, "authenticator3_options": null}, "GUJARAT_GAS_COMPANY_LTD": {"name": "Gujarat Gas company Limited", "authenticator3": null, "label": "Service Number (1-15 digits)", "note": null, "account_label": null, "authenticator3_options": null}, "VADODARA_GAS_LTD": {"name": "Aavantika Gas Ltd", "authenticator3": null, "label": "Customer Number (10-15 Alphanumeric)", "note": null, "account_label": null, "authenticator3_options": null}, "SGL": {"name": "Sabarmati Gas Limited (SGL)", "authenticator3": null, "label": "Customer ID (12 digits)", "note": null, "account_label": null, "authenticator3_options": null}, "SITI_ENERGY": {"name": "Siti Energy", "authenticator3": null, "label": "ARN Number (7 - 9 digits)", "note": null, "account_label": null, "authenticator3_options": null}, "UCPGPL": {"name": "Unique Central Piped Gases Pvt Ltd (UCPGPL)", "authenticator3": null, "label": "Customer Number (8 digits + character)", "note": null, "account_label": null, "authenticator3_options": null}, "IGL": {"name": "IGL (Indraprasth Gas Limited)", "authenticator3": null, "label": "Consumer Number (10 digits)", "note": null, "account_label": null, "authenticator3_options": null}, "ADANI_GAS": {"name": "ADANI GAS", "authenticator3": null, "label": "Customer ID (10 digits)", "note": null, "account_label": null, "authenticator3_options": null}}}

import 'dart:convert';
import 'package:http/http.dart' as http;

import 'package:flutter/material.dart';

class JsonApiDropdown extends StatefulWidget {
  @override
  JsonApiDropdownState createState() {
    return new JsonApiDropdownState();
  }
}

class JsonApiDropdownState extends State<JsonApiDropdown> {
  Operators _currentOperator;
  final String uri = "https://stage.linq.store/recharge-bill-payments?load=GetOperators&type=GAS";

  Future<List<Operators>> _fetchdata() async {
    var response = await http.get(uri, 
      headers: {
        "Content-Type" : "application/json",
        "MyToken" : "Token d5c9912f-4d4a-4776-88c4-545779804040",
        "Request-Type" : "mobile_api",
      }
    );

    if (response.statusCode == 200) {
      final operators = jsonDecode(response.body).cast<Map<String, dynamic>>();
      List<Operators> listOfOperators = operators.map<Operators>((json) {
        return Operators.fromJson(json);
      }).toList();

      return listOfOperators;
    } else {
      throw Exception('Failed to load internet');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Gas Bill Payment'),
        backgroundColor: Colors.orangeAccent,
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          mainAxisSize: MainAxisSize.max,
          children: <Widget>[
            FutureBuilder<List<Operators>>(
                future: _fetchdata(),
                builder: (BuildContext context,
                    AsyncSnapshot<List<Operators>> snapshot) {
                      if (!snapshot.hasData) return CircularProgressIndicator();
                      return DropdownButton<Operators>(
                        items: snapshot.data
                            .map((operator) => DropdownMenuItem<Operators>(
                                  child: Text(operator.name),
                                  value: operator,
                                ))
                            .toList(),
                        onChanged: (Operators value) {
                          setState(() {
                            _currentOperator = value;
                          });
                        },
                        isExpanded: false,
                        value: _currentOperator,
                        hint: Text('Select Operator'),
                        );
                }),
                SizedBox(height: 20.0),
                _currentOperator != null
                    ? Text("Name: " +
                        _currentOperator.name +
                        "\n Label: " +
                        _currentOperator.label +
                        "\n Account Label: " +
                        _currentOperator.accountLabel)
                    : Text("No Operator selected"),
          ],
        ),
      ),
    );
  }
}

class Operators {
  String name;
  String label;
  String accountLabel;

  Operators({
    this.name,
    this.label,
    this.accountLabel
  });

  factory Operators.fromJson(Map<String, dynamic> json) {
    return Operators(
      name: json['name'],
      label: json['label'],
      accountLabel: json['account_label'],
    );
  }
}
Ankit Singh
  • 143
  • 3
  • 11
  • do you get through the `== 200` test? – Richard Heap Feb 17 '20 at 01:04
  • Yes, because if it didnt get through, an exception would have been thrown which wasnt the case. Somehow, listOfOperators is blank. Either a problem with json.decode or maping. @RichardHeap – Ankit Singh Feb 17 '20 at 03:37
  • When I comment out the line if (!snapshot.hasData) return CircularProgressIndicator(); then an exception is thrown which reads NoSuchMethodError: invalid member on null: 'map' – Ankit Singh Feb 17 '20 at 03:46
  • Whenever I use builder, I always check the case where snapshot == null || snapshot.data == null. After that I use the data. I have never met any issues. – Hoàn Lê Feb 17 '20 at 05:11
  • @HoànLê i tried using that as well. the point is that snapshot is null and hence i cannot build the dropdown. any suggestions why it could be null. – Ankit Singh Feb 17 '20 at 05:16
  • Hi! When you parse the json response `return Operators.fromJson(json);`. Are you sure json has any value? Does the console prints any error? – Sebastian Feb 17 '20 at 14:02
  • @Sebastian As per the json data returned from the response, i think json.decode(response.body)['data'] will be the correct way. i will include the json data in the body. Please have a look and suggest. – Ankit Singh Feb 17 '20 at 18:59

1 Answers1

0

Created my own example based on you json, and loaded your json locally ,else everything is the same. check out the code below i have provided the json, model class and the main dart file.

Your json file below:

{
    "status": "success",
    "OperatorType": "GAS",
    "data": {
        "MAHANAGAR_GAS_LTD": {
            "name": "Mahanagar Gas Limited",
            "authenticator3": null,
            "label": "Customer Account Number (12 digits start with 21)",
            "note": null,
            "account_label": "Bill Group Number (1 - 8 characters + digits)",
            "authenticator3_options": null
        },
        "TRIPURA_NATURAL_GAS_COMPANY_LTD": {
            "name": "Tripura Natural Gas Company Ltd",
            "authenticator3": null,
            "label": "Consumer Number (1 - 20 digits)",
            "note": null,
            "account_label": null,
            "authenticator3_options": null
        },
        "HARAYANA_CITY_GAS": {
            "name": "Haryana City gas",
            "authenticator3": null,
            "label": "CRN Number (8-12 digits)",
            "note": null,
            "account_label": null,
            "authenticator3_options": null
        },
        "GUJARAT_GAS_COMPANY_LTD": {
            "name": "Gujarat Gas company Limited",
            "authenticator3": null,
            "label": "Service Number (1-15 digits)",
            "note": null,
            "account_label": null,
            "authenticator3_options": null
        },
        "VADODARA_GAS_LTD": {
            "name": "Aavantika Gas Ltd",
            "authenticator3": null,
            "label": "Customer Number (10-15 Alphanumeric)",
            "note": null,
            "account_label": null,
            "authenticator3_options": null
        },
        "SGL": {
            "name": "Sabarmati Gas Limited (SGL)",
            "authenticator3": null,
            "label": "Customer ID (12 digits)",
            "note": null,
            "account_label": null,
            "authenticator3_options": null
        },
        "SITI_ENERGY": {
            "name": "Siti Energy",
            "authenticator3": null,
            "label": "ARN Number (7 - 9 digits)",
            "note": null,
            "account_label": null,
            "authenticator3_options": null
        },
        "UCPGPL": {
            "name": "Unique Central Piped Gases Pvt Ltd (UCPGPL)",
            "authenticator3": null,
            "label": "Customer Number (8 digits + character)",
            "note": null,
            "account_label": null,
            "authenticator3_options": null
        },
        "IGL": {
            "name": "IGL (Indraprasth Gas Limited)",
            "authenticator3": null,
            "label": "Consumer Number (10 digits)",
            "note": null,
            "account_label": null,
            "authenticator3_options": null
        },
        "ADANI_GAS": {
            "name": "ADANI GAS",
            "authenticator3": null,
            "label": "Customer ID (10 digits)",
            "note": null,
            "account_label": null,
            "authenticator3_options": null
        }
    }
}

Model class defined for the json you provided :

// To parse this JSON data, do
//
//     final operators = operatorsFromJson(jsonString);

import 'dart:convert';

Operators operatorsFromJson(String str) => Operators.fromJson(json.decode(str));

String operatorsToJson(Operators data) => json.encode(data.toJson());

class Operators {
  String status;
  String operatorType;
  Map<String, Datum> data;

  Operators({
    this.status,
    this.operatorType,
    this.data,
  });

  factory Operators.fromJson(Map<String, dynamic> json) => Operators(
        status: json["status"],
        operatorType: json["OperatorType"],
        data: Map.from(json["data"])
            .map((k, v) => MapEntry<String, Datum>(k, Datum.fromJson(v))),
      );

  Map<String, dynamic> toJson() => {
        "status": status,
        "OperatorType": operatorType,
        "data": Map.from(data)
            .map((k, v) => MapEntry<String, dynamic>(k, v.toJson())),
      };
}

class Datum {
  String name;
  dynamic authenticator3;
  String label;
  dynamic note;
  String accountLabel;
  dynamic authenticator3Options;

  Datum({
    this.name,
    this.authenticator3,
    this.label,
    this.note,
    this.accountLabel,
    this.authenticator3Options,
  });

  factory Datum.fromJson(Map<String, dynamic> json) => Datum(
        name: json["name"],
        authenticator3: json["authenticator3"],
        label: json["label"],
        note: json["note"],
        accountLabel:
            json["account_label"] == null ? null : json["account_label"],
        authenticator3Options: json["authenticator3_options"],
      );

  Map<String, dynamic> toJson() => {
        "name": name,
        "authenticator3": authenticator3,
        "label": label,
        "note": note,
        "account_label": accountLabel == null ? null : accountLabel,
        "authenticator3_options": authenticator3Options,
      };
}

And this is the main file that i have written for the dropdown with your data

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:sample_testing_project/models.dart';

main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  List<Datum> data = List();
  bool _isLoading = false;
  String selectedOperator;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    loadYourData();
  }

  Future<String> loadFromAssets() async {
    return await rootBundle.loadString('json/parse.json');
  }

  loadYourData() async {
    setState(() {
      _isLoading = true;
    });
    // Loading your json locally you can make an api call, when you get the response just pass it to the operatorsFromJson method
    String jsonString = await loadFromAssets();
    final operators = operatorsFromJson(jsonString);

    operators.data.forEach((k, v) {
      data.add(v);
    });

    print(data.length);
    setState(() {
      _isLoading = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: _isLoading
            ? Text('Loading')
            : Container(
                child: Padding(
                  padding: const EdgeInsets.all(30.0),
                  child: Container(
                    height: 50,
                    decoration: BoxDecoration(
                      borderRadius: BorderRadius.circular(5.0),
                      border: Border.all(
                          color: Colors.red,
                          style: BorderStyle.solid,
                          width: 0.80),
                    ),
                    child: DropdownButton(
                        value: selectedOperator,
                        isExpanded: true,
                        icon: Padding(
                          padding: const EdgeInsets.only(left: 15.0),
                          child: Icon(Icons.arrow_drop_down),
                        ),
                        iconSize: 25,
                        underline: SizedBox(),
                        onChanged: (newValue) {
                          setState(() {
                            selectedOperator = newValue;
                          });
                        },
                        hint: Padding(
                          padding: const EdgeInsets.all(8.0),
                          child: Text('Select'),
                        ),
                        items: data.map((data) {
                          return DropdownMenuItem(
                            value: data.name,
                            child: Padding(
                              padding: const EdgeInsets.only(left: 10.0),
                              child: Text(
                                data.name,
                                style: TextStyle(
                                  fontSize: 18,
                                  color: Colors.black,
                                ),
                              ),
                            ),
                          );
                        }).toList()),
                  ),
                ),
              ),
      ),
    );
  }
}

I have loaded your json locally from the loadFromAssets method but you can call your api and then pass the response.body to operatorsFromJson method.

Else every thing is the same, i have just removed every value (thats object) from the key,value map and the addded in the list and displayed it in the dropdown. Let me know if it works.

Sagar Acharya
  • 3,397
  • 4
  • 12
  • 34