While trying to retrieve categories from the categories endpoint, I have ran into this error: _TypeError (type 'List<dynamic>' is not a subtype of type 'Map<String, dynamic>' in type cast)
This error is thrown by the return in the extension from the myprojectname_api_client.dart:
Future<CategoriesResponse> getCategories() async {
final uri = Uri.parse('$_baseUrl/categories');
final response = await _httpClient.get(
uri,
headers: await _getRequestHeaders(),
);
final body = response.json();
if (response.statusCode != HttpStatus.ok) {
throw BernardinaiApiRequestFailure(
body: body,
statusCode: response.statusCode,
);
}
return CategoriesResponse.fromJson(body);
}
//------------
extension on http.Response {
Map<String, dynamic> json() {
try {
final decodedBody = utf8.decode(bodyBytes);
log("MyLog $body");
return jsonDecode(decodedBody) as Map<String, dynamic>;
} catch (error, stackTrace) {
Error.throwWithStackTrace(
BernardinaiApiMalformedResponse(error: error),
stackTrace,
);
}
}
}
The log shows that the decodeBody
contains the full JSON from the categories endpoints.
The getCategories
method in my custom class that implements NewsDataSource
class looks like this:
Future<List<Category>> getCategories() async {
final uri = Uri.parse('https://www.mynewsurl.com/wp-json/wp/v2/categories');
final categoriesResponse = await http.get(uri);
if (categoriesResponse.statusCode == HttpStatus.ok) {
// Parse the response body into a Map
final Map<String, dynamic> categoriesData = jsonDecode(categoriesResponse.body) as Map<String, dynamic>;
// Access the 'categories' list from the map
final List<dynamic> categoriesList = categoriesData['categories'] as List<dynamic>;
log("MyLog categoriesList: $categoriesList");
// Extract category names and return them as a list of Category enums
final List<Category> categories = categoriesList.map((dynamic categoryData) {
final String categoryName = categoryData as String; // Explicitly cast to String
log("MyLog categoryName: $categoryName");
return Category.fromString(categoryName); // Use fromString to get the enum
}).toList();
log("MyLog categories to return: $categories");
return categories; // Returns list of category names
} else {
// Handle error cases by throwing a custom exception
throw BernardinaiApiRequestFailure(
statusCode: categoriesResponse.statusCode,
body: jsonDecode(categoriesResponse.body) as Map<String, dynamic>,
);
}
}
///------
class CategoriesInfo {
const CategoriesInfo(this.slug);
final String slug;
CategoriesInfo.fromJson(Map<String, dynamic> json)
: slug = _isKebabCase(json['slug'] as String)
? _convertToCamelCase(json['slug'] as String)
: json['slug'] as String;
Map<String, dynamic> toJson() {
log("mano as veikiu");
return {
'slug': slug,
};
}
static bool _isKebabCase(String input) {
log("MyLog checking what case is used");
return input.contains('-');
}
static String _convertToCamelCase(String input) {
log("MANO slugas: $input");
List<String> parts = input.split('-');
if (parts.length == 1) return input;
String camelCase = parts[0];
for (int i = 1; i < parts.length; i++) {
camelCase += parts[i][0].toUpperCase() + parts[i].substring(1);
}
return camelCase;
}
}
I tried to change the extension type to List, but then all the other methods are throwing the same error, so now I am wondering if the Future<List<Category>> getCategories()
should not be a list but a map? But it was defined like that in the template, so maybe it's one of the bugs? Or perhaps I am not seeing something? I am completely lost and the documentation is of no help to me, so any help will be appreciated.
After analysing my code, I found that the problem was the logic I was trying to implement - the link inside the Future<CategoriesResponse> getCategories()
should have been the link to the server that formats data to blocks that the app is expecting, and not to the JSON data source.