1
votes

I'm new to programming world, I have do research from many source about this error but i've found nothing. I'm trying to build a ListView.builder in Flutter, where the itemBuilder is from my JSON response data like this:

{
  "status": "success",
  "data": {
    "general": [
      {
        "id": 1,
        "name": "Sumbangan Pembinaan Pendidikan",
        "icon": "credit_card",
        "type": "monthly",
        "amount": 125000
      },
      {
        "id": 2,
        "name": "Uang Bangunan",
        "icon": "credit_card",
        "type": "yearly",
        "amount": 1250000
      }
    ],
    "categorized": [
      {
        "name": "Bayar Buku",
        "icon": "credit_card",
        "childs": [
          {
            "id": 3,
            "name": "Buku 1",
            "icon": "credit_card",
            "type": "monthly",
            "amount": 324423
          },
          {
            "id": 4,
            "name": "Buku 2",
            "icon": "credit_card",
            "type": "monthly",
            "amount": 16000
          }
        ]
      }
    ]
  }
}

I need to get the 'name' of item to fetch with my ListView.builder, This is what I've come up with

import 'dart:convert';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:sekolah_kita/components/constant.dart';
import 'package:http/http.dart' as http;
import 'package:sekolah_kita/components/storage.dart';

class DaftarTransaksi extends StatefulWidget {
  @override
  _DaftarTransaksiState createState() => _DaftarTransaksiState();
}

class _DaftarTransaksiState extends State<DaftarTransaksi> {
  final SecureStorage secureStorage = SecureStorage();

  List studentFeesData;

  bool isLoading = true;

  @override
  void initState() {
    secureStorage.readSecureData('student_token').then((value) {
      getStudentFees(
        value,
      );
    });
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: secondaryColor,
      appBar: AppBar(
        leading: IconButton(
          onPressed: (){
            Navigator.pop(context);
          },
          icon: Icon(
            Icons.arrow_back
          ),
        ),
        backgroundColor: primaryColor,
        elevation: 0,
        centerTitle: true,
        title: Text(
          'Transaksi',
          style: TextStyle(
            fontSize: screenWidth(context)*(1/25),
          ),
        ),
      ),
      body: isLoading ? Center(
        child: CircularProgressIndicator(
          backgroundColor: primaryColor,
        ),
      ) : Center(
        child: Container(
          margin: EdgeInsets.symmetric(
            vertical: screenHeight(context)*(1/30),
            horizontal: screenWidth(context)*(1/20),
          ),
          color: Colors.green.withOpacity(0.5),
          child: ListView.builder(
            itemCount: studentFeesData == 0 ? 0 : studentFeesData.length,
            itemBuilder: (context, index){
              return studentFeeButtonMenu(
                context, 
                studentFeesData[index]['data']['general']['name'], 
                Icons.credit_card);
            },
          ),
        ),
      ),
    );
  }

  Future<String> getStudentFees(String token) async{
    var uri = Uri.https('sekolahkita.zonaku.com', '/api/school-fee/bill');
    http.Response response = await http.get(
      uri,
      headers: {
        HttpHeaders.contentTypeHeader: 'application/json',
        HttpHeaders.acceptHeader: 'application/json',
        HttpHeaders.authorizationHeader: "Bearer "+token,
      },
    );
    var data = json.decode(response.body);
    studentFeesData = List<dynamic>.from(
      data.map<dynamic>(
        (dynamic item) => item,
      )
    );
  }

  Widget studentFeeButtonMenu(BuildContext context, String text, IconData iconFee){
    return Container(
      width: double.infinity,
      height: screenHeight(context)*(1/12),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(10),
      ),
      child: Center(
        child: Container(
          width: screenWidth(context)*(1/1.3),
          height: double.infinity,
          color: Colors.red,
          child: Row(
            children: [
              Icon(
                iconFee,
                color: Color(0xff84923f),
              ),
              SizedBox(
                width: screenWidth(context)*(1/10),
              ),
              Text(
                text,
                style: TextStyle(
                  color: Colors.black,
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

}

But I've always get an error to display what i want in ListView.builder. The runtime type of my JSON response is '_InternalLinkedHashMap<String, dynamic>', and I know I need to convert it to List, so it can be fitted with studentFeesData variable to display it in ListView.builder.

This is my error message:

Exception has occurred. NoSuchMethodError (NoSuchMethodError: Class '_InternalLinkedHashMap<String, dynamic>' has no instance method 'map' with matching arguments. Receiver: _LinkedHashMap len:2 Tried calling: map(Closure: (dynamic) => dynamic) Found: map<K2, V2>((K, V) => MapEntry<K2, V2>) => Map<K2, V2>)

I hope anyone can help me with this.

1

1 Answers

0
votes

You need to convert your json data to a model object for easier access. I have converted your json data as follows:

StudentFeesModel
GeneralModel
CategorizedModel

Now, you can access & iterate over the list of GeneralModel & CategorizedModel to get the names of the children.

Here is the snippet:

import 'dart:convert';

void main() {
  dynamic data = {
    "status": "success",
    "data": {
      "general": [
        {
          "id": 1,
          "name": "Sumbangan Pembinaan Pendidikan",
          "icon": "credit_card",
          "type": "monthly",
          "amount": 125000
        },
        {
          "id": 2,
          "name": "Uang Bangunan",
          "icon": "credit_card",
          "type": "yearly",
          "amount": 1250000
        }
      ],
      "categorized": [
        {
          "name": "Bayar Buku",
          "icon": "credit_card",
          "childs": [
            {
              "id": 3,
              "name": "Buku 1",
              "icon": "credit_card",
              "type": "monthly",
              "amount": 324423
            },
            {
              "id": 4,
              "name": "Buku 2",
              "icon": "credit_card",
              "type": "monthly",
              "amount": 16000
            }
          ]
        }
      ]
    }
  };

  // NOTE: You just need to pass data instead of data["data"] i.e,
  // You should write the following:
  // StudentFeesModel studentFeesData = StudentFeesModel.fromJson(data);
  StudentFeesModel studentFeesData = StudentFeesModel.fromJson(data["data"]);
  
  List generalNames = studentFeesData.general.map((generalModel) => generalModel.name).toList();
  List categorizedNames = studentFeesData.categorized.map((categorizedModel) => categorizedModel.name).toList();
  
  print("General names: " + generalNames.toString());
  print("Categorized names: " + categorizedNames.toString());
  
  // If you want categorized child names, then
  // Iterate over all categorized objects & add all child names to a single list
  
  List categorizedChildNames = [];
  for(dynamic categorized in studentFeesData.categorized) {
    categorizedChildNames.addAll(categorized.childs.map((childObject) => childObject.name).toList());
  }
  
  print("Categorized child names: " + categorizedChildNames.toString());
}

// **************************
// Model classes
// **************************
class StudentFeesModel {
  StudentFeesModel({
    this.general,
    this.categorized,
  });

  final List<dynamic> general, categorized;

  factory StudentFeesModel.fromJson(dynamic json) {
    return StudentFeesModel(
      general: GeneralModel.listOfGeneralModel(json["general"]),
      categorized: CategorizedModel.listOfCategorizedModel(json["categorized"]),
    );
  }
  
  dynamic toJson() => {
    "general": general,
    "categorized": categorized,
  };
  
  @override
  String toString() {
    return '${JsonEncoder.withIndent(' ').convert(this)}';
  }
}

class GeneralModel {
  GeneralModel({
    this.id,
    this.name,
    this.icon,
    this.type,
    this.amount,
  });

  final int id, amount;
  final String name, icon, type;

  factory GeneralModel.fromJson(dynamic json) {
    if (json == null) return null;

    return GeneralModel(
      id: json["id"],
      name: json["name"],
      icon: json["icon"],
      type: json["type"],
      amount: json["amount"],
    );
  }

  static List<dynamic> listOfGeneralModel(dynamic list) {
    if (list == null) return null;

    dynamic generalModelList = [];
    for (dynamic json in list) {
      generalModelList.add(GeneralModel.fromJson(json));
    }

    return generalModelList;
  }
  
  dynamic toJson() => {
    "id": id,
    "name": name,
    "icon": icon,
    "type": type,
    "amount": amount,
  };
  
  @override
  String toString() {
    return '${JsonEncoder.withIndent(' ').convert(this)}';
  }
}

class CategorizedModel {
  CategorizedModel({
    this.name,
    this.icon,
    this.childs, // children would be more appropriate
  });

  final String name, icon;
  final List<dynamic> childs; // children would be more appropriate

  factory CategorizedModel.fromJson(dynamic json) {
    return CategorizedModel(
      name: json["name"],
      icon: json["icon"],
      childs: GeneralModel.listOfGeneralModel(json["childs"]), // children would be more appropriate
    );
  }

  static List<dynamic> listOfCategorizedModel(List<dynamic> list) {
    if (list == null) return null;

    List categorizedModelList = [];
    for (dynamic json in list) {
      categorizedModelList.add(CategorizedModel.fromJson(json));
    }
    
    return categorizedModelList;
  }
  
  dynamic toJson() => {
    "name": name,
    "icon": icon,
    "childs": childs,
  };
  
  @override
  String toString() {
    return '${JsonEncoder.withIndent(' ').convert(this)}';
  }
}