I am consuming data from an API and whenever I try to do so, I keep getting this error. Type List Dynamic Is Not A Subtype Of Type Map String Dynamic
In my attempt to find answers, I came across this Similar Question
I have also gone through this Another Similar Question
And this as well A similar question again
From this similar question, I realized that there seems to be a data structure mismatch but I can't seem to get the solution to it.
Below are excerpts of my code
This is the Object Model
class Book {
int? id = 0;
String? title = "";
String? description = "";
String? image = "";
String? author = "";
Book({
this.id,
this.title,
this.description,
this.image,
this.author,
});
Book.fromJson(Map<String, dynamic> parsedJson) {
id = parsedJson['id'];
title = parsedJson['title'];
description = parsedJson['description'];
image = parsedJson['image'];
author = parsedJson['author'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['title'] = this.title;
data['description'] = this.description;
data['image'] = this.image;
data['author'] = this.author;
return data;
}
}
This is the Controller Class that seems to contain the error. I am able to print the content coming from the backend though.
import 'dart:io';
import 'package:elibrary/model/books.dart';
import 'package:elibrary/services/repository/book_repository.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class BookController extends GetxController {
BookRepository bookRepository = BookRepository();
RxBool isLoading = false.obs;
RxList<Book> bookList = <Book>[].obs;
@override
void onInit() {
super.onInit();
fetchBooksList();
}
Future<void> fetchBooksList() async {
isLoading(true);
try {
Response bookResponse = await bookRepository.fetchBooks();
if (bookResponse.statusCode == 200) {
for (var element in bookResponse.body) {
bookList.add(Book.fromJson(element));
}
} else {
Get.snackbar(
'Error Occurred',
bookResponse.statusText.toString(),
snackPosition: SnackPosition.BOTTOM,
colorText: Colors.white,
backgroundColor: Colors.red,
);
}
} catch (e) {
debugPrint(
e.toString(),
);
Get.snackbar(
"Error Occurred",
e.toString(),
snackPosition: SnackPosition.BOTTOM,
colorText: Colors.white,
backgroundColor: Colors.green,
duration: Duration(seconds: 5),
).show();
} finally {
isLoading(false);
}
}
}
I did try changing the model object to this
import 'dart:convert';
Book bookFromJson(String str) => Book.fromJson(json.decode(str));
String bookToJson(Book data) => json.encode(data.toJson());
class Book {
Book({
this.id,
this.title,
this.description,
this.image,
this.author,
});
int id;
String title;
String description;
String image;
String author;
factory Book.fromJson(Map<String, dynamic> json) => Book(
id: json["id"],
title: json["title"],
description: json["description"],
image: json["image"],
author: json["author"],,
);
Map<String, dynamic> toJson() => {
"id": id,
"title": title,
"description": description,
"image": image,
"author": author,
};
}
And then I tried the controller this way also
import 'dart:io';
import 'package:elibrary/model/books.dart';
import 'package:elibrary/services/repository/book_repository.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class BookController extends GetxController {
BookRepository bookRepository = BookRepository();
RxBool isLoading = false.obs;
RxList<Book> bookList = <Book>[].obs;
@override
void onInit() {
super.onInit();
fetchBooksList();
}
Future<void> fetchBooksList() async {
isLoading(true);
try {
Response bookResponse = await bookRepository.fetchBooks();
if (bookResponse.statusCode == 200) {
bookList.assAll(
bookFromJson(bookResponse.bodyString ?? ''),
)
} else {
Get.snackbar(
'Error Occurred',
bookResponse.statusText.toString(),
snackPosition: SnackPosition.BOTTOM,
colorText: Colors.white,
backgroundColor: Colors.red,
);
}
} catch (e) {
debugPrint(
e.toString(),
);
Get.snackbar(
"Error Occurred",
e.toString(),
snackPosition: SnackPosition.BOTTOM,
colorText: Colors.white,
backgroundColor: Colors.green,
duration: Duration(seconds: 5),
).show();
} finally {
isLoading(false);
}
}
}
```
```
Again I tried decoding the response this way
```
```
var jsonList = jsonDecode(bookResponse.bodyString ?? '')
.map((book) => Book.fromJson(book))
.toList();
bookList.assignAll(jsonList);
debugPrint('Total Book List is: ${bookList.length}');
All these attempts produce the same error.
These is the API Response
I/flutter ( 5788): key = data, value = [{id: 1, name: dolore, icon: http://192.168.1.102:8000/images/categories/https://via.placeholder.com/640x480.png/007766?text=architecto}, {id: 2, name: repellat, icon: http://192.168.1.102:8000/images/categories/https://via.placeholder.com/640x480.png/004444?text=voluptatum}, {id: 3, name: est, icon: http://192.168.1.102:8000/images/categories/https://via.placeholder.com/640x480.png/005577?text=et}, {id: 4, name: quasi, icon: http://192.168.1.102:8000/images/categories/https://via.placeholder.com/640x480.png/00cc00?text=deserunt}, {id: 5, name: provident, icon: http://192.168.1.102:8000/images/categories/https://via.placeholder.com/640x480.png/008888?text=et}, {id: 6, name: quo, icon: http://192.168.1.102:8000/images/categories/https://via.placeholder.com/640x480.png/007777?text=dolorem}, {id: 7, name: expedita, icon: http://192.168.1.102:8000/images/categories/https://via.placeholder.com/640x480.png/00aa88?text=adipisci}, {id: 8, name: quia, icon: http://192.168.1.102:8000/images/categor
I/flutter ( 5788): result = {"data":[{"id":1,"name":"dolore","icon":"http:\/\/192.168.1.102:8000\/images\/categories\/https:\/\/via.placeholder.com\/640x480.png\/007766?text=architecto"},{"id":2,"name":"repellat","icon":"http:\/\/192.168.1.102:8000\/images\/categories\/https:\/\/via.placeholder.com\/640x480.png\/004444?text=voluptatum"},{"id":3,"name":"est","icon":"http:\/\/192.168.1.102:8000\/images\/categories\/https:\/\/via.placeholder.com\/640x480.png\/005577?text=et"},{"id":4,"name":"quasi","icon":"http:\/\/192.168.1.102:8000\/images\/categories\/https:\/\/via.placeholder.com\/640x480.png\/00cc00?text=deserunt"},{"id":5,"name":"provident","icon":"http:\/\/192.168.1.102:8000\/images\/categories\/https:\/\/via.placeholder.com\/640x480.png\/008888?text=et"},{"id":6,"name":"quo","icon":"http:\/\/192.168.1.102:8000\/images\/categories\/https:\/\/via.placeholder.com\/640x480.png\/007777?text=dolorem"},{"id":7,"name":"expedita","icon":"http:\/\/192.168.1.102:8000\/images\/categories\/https:\/\/via.placeholder.com\/640x480.png\/0
I/flutter ( 5788): Total Book List is: 0
I/flutter ( 5788): type 'List<dynamic>' is not a subtype of type 'Map<String, dynamic>'
CodePudding user response:
Your response is map
, you need use ["data"]
on it to get your list you want. So in your Book
class change this:
Book bookFromJson(String str) => Book.fromJson(json.decode(str));
to this:
List<Book> bookFromJson(String str) => (json.decode(str)["data"] as List).map((e) => Book.fromJson(e)).toList();
CodePudding user response:
You are receiving a JSON list and you have to pass each item of it to Book.parseJson
function.
List<Book> bookFromJson(String str) {
Iterable jsonArray = json.decode(str);
return List<Book>.from(jsonArray.map((json) => Book.fromJson(json)));
}