How to fix Flutter Expected a value of type 'Map<String, dynamic>', but got one of type 'List<dynamic>'

Viewed 22651

I'm developing a web app using Flutter Web and RESTful API for backend. So, I'm trying the fetch the data from the api, serialize it by using Flutter Models, then return the result.

The Problem is, I'm getting this result

Expected a value of type 'Map<String, dynamic>', but got one of type 'List<dynamic>'

How to fix this ?

Here's my flutter codes:

models

// To parse this JSON data, do
//
//     final medicalRecordsModel = medicalRecordsModelFromJson(jsonString);

import 'dart:convert';

class MedicalRecordsModel {
  MedicalRecordsModel({
    this.id,
    this.category,
    this.fileName,
    this.dateTimestamp,
    this.description,
    this.upload,
    this.patientName,
    this.age,
    this.address,
    this.userId,
    this.patientId,
    this.isActive,
  });

  final String id;
  final String category;
  final String fileName;
  final String dateTimestamp;
  final String description;
  final String upload;
  final String patientName;
  final String age;
  final String address;
  final dynamic userId;
  final int patientId;
  final bool isActive;

  factory MedicalRecordsModel.fromJson(Map<String, dynamic> json) {
    return MedicalRecordsModel(
      id: json["id"],
      category: json["category"],
      fileName: json["fileName"],
      dateTimestamp: json["dateTimestamp"],
      description: json["description"],
      upload: json["upload"],
      patientName: json["patientName"],
      age: json["age"],
      address: json["address"],
      userId: json["userId"],
      patientId: json["patientId"],
      isActive: json["isActive"],
    );
  }
}

API Connection

import 'dart:convert';
import 'dart:developer';
import 'dart:async';
import 'package:app/src/constants/medical_records.dart';
import 'package:app/src/models/medical_records/medical_records.dart';
import 'package:app/src/pages/Medical-Records/medical_record.dart';
import 'package:http/http.dart' as http;

class MedicalRecordsManager {
  var client = http.Client();
  var url = ConstantMedicalRecords.medical_records_api;

  Future<MedicalRecordsModel> getRecords() async {
    var url = ConstantMedicalRecords.medical_records_api;
    log('$url');
    try {
      final response = await client.get(url);
      if (response.statusCode == 200) {
        return MedicalRecordsModel.fromJson(jsonDecode(response.body));
        // print(recordsModel);
      }
    } catch (Exception) {
      print(Exception);
      print("Error occured");
    }
  }
}


Here is the JSON data I want to get

 {
        "id": "103",
        "category": "DOCUMENT",
        "fileName": "Check Up",
        "dateTimestamp": "2021-02-1012:59:46",
        "description": "string",
        "upload": "String",
        "patientName": "1",
        "age": "25",
        "address": "Earth",
        "userId": null,
        "patientId": 12,
        "isActive": true
    }

Please help me with this one.

4 Answers

you can do it like that

MedicalRecordsModel.fromJson(jsonDecode(response.body) as Map<String, dynamic>);

change the getRecord as follows

Future<MedicalRecordsModel> getRecords() async {
    var url = ConstantMedicalRecords.medical_records_api;
    log('$url');
    try {
      final response = await client.get(url);
      if (response.statusCode == 200) {
        return MedicalRecordsModel.fromJson(jsonDecode(response.body)[0]);
        // print(recordsModel);
      }
    } catch (Exception) {
      print(Exception);
      print("Error occured");
    }
  }

I think jsonDecode gives list of Maps therefore your json map is the first element of that list.

This code wiil work as you expected:

import 'package:json_helpers/json_helpers.dart';

void main() {
  // responseBody is the same response.body

  // When response is a list of objects
  final list = responseBody1.jsonList((e) => MedicalRecordsModel.fromJson(e));
  var obj = list[0];
  print(obj.category);
  print(obj.fileName);

  // When response is an object
  obj = responseBody2.json((e) => MedicalRecordsModel.fromJson(e));
  print(obj.category);
  print(obj.fileName);
}

final responseBody1 = '''
[
   {
      "id":"103",
      "category":"DOCUMENT",
      "fileName":"Check Up",
      "dateTimestamp":"2021-02-1012:59:46",
      "description":"string",
      "upload":"String",
      "patientName":"1",
      "age":"25",
      "address":"Earth",
      "userId":null,
      "patientId":12,
      "isActive":true
   }
]''';

final responseBody2 = '''
{
   "id":"103",
   "category":"DOCUMENT",
   "fileName":"Check Up",
   "dateTimestamp":"2021-02-1012:59:46",
   "description":"string",
   "upload":"String",
   "patientName":"1",
   "age":"25",
   "address":"Earth",
   "userId":null,
   "patientId":12,
   "isActive":true
}''';

class MedicalRecordsModel {
  final String id;

  final String category;
  final String fileName;
  final String dateTimestamp;
  final String description;
  final String upload;
  final String patientName;
  final String age;
  final String address;
  final dynamic userId;
  final int patientId;
  final bool isActive;
  MedicalRecordsModel({
    this.id,
    this.category,
    this.fileName,
    this.dateTimestamp,
    this.description,
    this.upload,
    this.patientName,
    this.age,
    this.address,
    this.userId,
    this.patientId,
    this.isActive,
  });

  factory MedicalRecordsModel.fromJson(Map<String, dynamic> json) {
    return MedicalRecordsModel(
      id: json['id'] as String,
      category: json['category'] as String,
      fileName: json['fileName'] as String,
      dateTimestamp: json['dateTimestamp'] as String,
      description: json['description'] as String,
      upload: json['upload'] as String,
      patientName: json['patientName'] as String,
      age: json['age'] as String,
      address: json['address'] as String,
      userId: json['userId'] as String,
      patientId: json['patientId'] as int,
      isActive: json['isActive'] as bool,
    );
  }
}

Output:

DOCUMENT
Check Up
DOCUMENT
Check Up

That is, when response is a list of objects:

final list = response.body.jsonList((e) => MedicalRecordsModel.fromJson(e));

When response is an object:

final object = response.body.json((e) => MedicalRecordsModel.fromJson(e));

If you don't know what the result is, then you can try both methods.

response.body.json((e) => Model.fromJson(e));
response.body.jsonList((e) => Model.fromJson(e));

If you have already decoded a JSON string and want to convert the result (or part of it), you can use the following methods:

If the type of the decoded value is Map:

final object = value.json((e) => Model.fromJson(e));

If the type of the decoded value is List:

final objects = value.json((e) => Model.fromJson(e));

Every response is sended and received as text, which can be converted to the
Map Format with the dart inbuilt core library import 'dart:convert';.

So the response from the request can be treated like this.

final res = await http.post(Uri.parse(url),  body: json.encode({
          'userId': uid,
          'email': email,
        }),
        head body: json.encode({
          'userId': uid,
          'email': email,
        }),
        headers: {'Content-Type': 'application/json', 'token64': token});

here json.encode() is used to convert to String from Map.

now res variable contain the response which is also a string which can be convert to the Map with json.decode() like this.

final data = json.decode(res);

when working with the data sometimes we occur errors like Map is not a type of Map<String, String> etc.

Which can be solved by type casting the res, like this.

Map<String, String> notification = Map<String, String>.from(data['notification']);

I see these type casting method used in the The boring Flutter Development show in Youtube.

Related