Can't convert JSON list to list of photos in flutter
The problem most likely comes from this line.
@override
Future<List<Photo>> getPhotosList() async {
var result = _api.fetchDataByUrl(_url).then((data) {
var list = data?.map((el) => Photo.fromJson(el)).toList();
return list;
});
return result;
}
As I can see, the type of data
that is in then(), is dynamic, and you wanna .map
over it, so perhaps you may need to explicitly cast a type for it since it doesn't infer that.
So change this to
@override
Future<List<Photo>> getPhotosList() async {
final dynamic result = await _api.fetchDataByUrl(_url);
return (result as List<dynamic>).map((dynamic el) => Photo.fromJson(el as Map<String, dynamic>)).toList();
}
so I polished your code, I hope this can help you.
abstract class PhotosRepository {
Future<List<Photo>> getPhotosList();
}
class Photo {
Photo({
this.id,
this.title,
this.username,
this.url,
});
factory Photo.fromJson(Map<String, dynamic> json) {
return Photo(
id: json['id'] as String,
title: json['alt_description'] as String,
username: json['user']['name'] as String,
url: json['urls']['raw'] as String,
);
}
final String id;
final String title;
final String username;
final String url;
@override
String toString() {
return '''$id, $title, $username, $url''';
}
}
class UnsplashApi {
final String _clientId = "84y4hgje4ac868c2646c0eddjhfedihdhff612b04c264f3374c97fff98ed253dc9";
Future<String> _fetch(String url) async {
try {
url = buildUrl(url);
final http.Response response = await http.get(url);
if (response.statusCode == 200) {
return response.body;
}
return null;
} catch (e) {
print(e);
return null;
}
}
Future<dynamic> fetchDataByUrl(String url) async {
final String data = await _fetch(url);
try {
return json.decode(data);
} catch (e) {
print('Bad content, could not decode JSON $e');
}
}
String buildUrl(String url) {
return url.contains('?') ? '$url&client_id=$_clientId' : '$url?client_id=$_clientId';
}
}
class UnsplashPhotosRepository implements PhotosRepository {
UnsplashPhotosRepository(this._api);
final UnsplashApi _api;
final String _url = 'https://api.unsplash.com/photos';
@override
Future<List<Photo>> getPhotosList() async {
final dynamic result = await _api.fetchDataByUrl(_url);
return (result as List<dynamic>)?.map((dynamic el) => Photo.fromJson(el as Map<String, dynamic>))?.toList();
}
}
and the result will be
[nV8K0uguyiw, man in green zip up jacket beside woman in black shirt, XPS, https://images.unsplash.com/photo-1593643946890-b5b85ade6451?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjE1OTU4N30, YE_2tlbi-IM, woman in black and white striped long sleeve shirt wearing black framed eyeglasses, Hans Mendoza, https://images.unsplash.com/photo-1597982178640-358c9e03bfae?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjE1OTU4N30, cHRDevKFDBw, green palm trees near city buildings during daytime, Cameron Venti, https://images.unsplash.com/photo-1597982087634-9884f03198ce?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjE1OTU4N30, -O0kKUPxDAM, green and blue lighted building during night time, ZQ Lee, https://images.unsplash.com/photo-1597991840620-cecdef61763b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjE1OTU4N30, F_-Ehgu36_8, person holding red disposable cup with black straw, jo jo ◡̈, https://images.unsplash.com/photo-1597922650352-77f42b5b6571?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjE1OTU4N30, 3_SeMFFlNvU, woman in brown t-shirt and black shorts <…>
you may check the full code here as well
https://gist.github.com/mhadaily/d721979393dd031062d9e1674747762d
AndreyKo
Updated on December 23, 2022Comments
-
AndreyKo over 1 year
I'm learning flutter by making a simple photo gallery from unsplash service and trying to fetch a list of photos from https://api.unsplash.com/photos.
I'm getting the runtime error:
type 'Future<dynamic>' is not a subtype of type 'FutureOr<List<Photo>>'
Though if I print a list instead of returning it I get a list of instances of Photo in console. What's wrong with my code and how to do it right?
My photo model looks like this:
class Photo { final String id; final String title; final String username; final String url; Photo({this.id, this.title, this.username, this.url}); factory Photo.fromJson(Map<String, dynamic> json) { return Photo( id: json['id'], title: json['alt_description'], username: json['user']['name'], url: json['urls']['raw']); } }
My api data source:
import 'package:http/http.dart' as http; import 'dart:convert'; class UnsplashApi { final String _clientId = "84y4hgje4ac868c2646c0eddjhfedihdhff612b04c264f3374c97fff98ed253dc9"; Future<String> _fetch(String url) async { try { url = buildUrl(url); var response = await http.get(url); if (response.statusCode == 200) { return response.body; } } catch (e) { print(e); } } Future<dynamic> fetchDataByUrl(String url) async { var data = await _fetch(url); try { return json.decode(data); } catch (e) { print('Bad content, could not decode JSON'); } } String buildUrl(url) { return url.contains('?') ? url + '&client_id=$_clientId' : url + '?client_id=$_clientId'; } }
My repository contract (interface):
import 'package:unsplash/domain/models/photo.dart'; abstract class PhotosRepository { Future<List<Photo>> getPhotosList(); }
My Unsplash Photos repository:
import 'package:unsplash/domain/models/photo.dart'; import 'package:unsplash/domain/repositories/photos_repository.dart'; import 'package:unsplash/infrastructure/data_sources/unsplash_api.dart'; class UnsplashPhotosRepository implements PhotosRepository { final UnsplashApi _api; final String _url = 'https://api.unsplash.com/photos'; UnsplashPhotosRepository(this._api); @override Future<List<Photo>> getPhotosList() async { var result = _api.fetchDataByUrl(_url).then((data) { var list = data?.map((el) => Photo.fromJson(el)).toList(); return list; }); return result; } }
And My test:
import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:unsplash/infrastructure/data_sources/unsplash_api.dart'; import 'package:unsplash/infrastructure/repositories/unsplash_photos_repository.dart'; void main() { group('Unsplash api', () { test('should get search results', () async { final api = UnsplashApi(); var result = await UnsplashPhotosRepository(api).getPhotosList(); print(result.runtimeType); expect(result.isEmpty, false); }); }); }
-
AndreyKo over 3 yearsHi, @Majid, I tried to replace the line you provided but got a compile error: Too many positional arguments: 1 expected, but 2 found.
-
Majid over 3 yearscheck this code gist.github.com/mhadaily/d721979393dd031062d9e1674747762d it works
-
AndreyKo over 3 yearsI've made a try with the line but the error is the same.. type 'Future<dynamic>' is not a subtype of type 'FutureOr<List<Photo>>' I'll checkout your link now..
-
AndreyKo over 3 yearsOh, thanks, it worked, my bad, I've missed (result as List<dynamic>) part, replaced only the rest of it. But anyway why is not it idiomatic according to the flutter documentation, I learned by looking at flutter.dev/docs/cookbook/networking/background-parsing
-
AndreyKo over 3 yearsOk, I've got it, my getDataByUrl() method explicitly returns Future<dynamic> because I want to fetch not only json lists but json objects as well. Therefore dart can't infer the list out of it.. Big thanks, Majid!