Can't convert JSON list to list of photos in flutter

232

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

Share:
232
AndreyKo
Author by

AndreyKo

Updated on December 23, 2022

Comments

  • AndreyKo
    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
    AndreyKo over 3 years
    Hi, @Majid, I tried to replace the line you provided but got a compile error: Too many positional arguments: 1 expected, but 2 found.
  • Majid
    Majid over 3 years
  • AndreyKo
    AndreyKo over 3 years
    I'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
    AndreyKo over 3 years
    Oh, 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
    AndreyKo over 3 years
    Ok, 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!