Error fetching API / A value of type 'List<Heroes>' can't be returned from the method 'getHeroes' because has a return type of 'Future<List<String>?>

Viewed 40

im new in Dart/Flutter and im struggling with consuming API, here is my file thats inside my model folder:

List<Heroes> heroesFromJson(String str) =>
    List<Heroes>.from(json.decode(str).map((x) => Heroes.fromJson(x)));

String heroesToJson(List<Heroes> data) =>
    json.encode(List<dynamic>.from(data.map((x) => x.toJson())));

class Heroes {
  Heroes({
    required this.id,
    required this.name,
    required this.localizedName,
    required this.primaryAttr,
    required this.attackType,
    required this.roles,
  });

  int id;
  String name;
  String localizedName;
  String primaryAttr;
  String attackType;
  List<String> roles;

  factory Heroes.fromJson(Map<String, dynamic> json) => Heroes(
        id: json["id"],
        name: json["name"],
        localizedName: json["localized_name"],
        primaryAttr: json["primary_attr"],
        attackType: json["attack_type"],
        roles: List<String>.from(json["roles"].map((x) => x)),
      );

  Map<String, dynamic> toJson() => {
        "id": id,
        "name": name,
        "localized_name": localizedName,
        "primary_attr": primaryAttr,
        "attack_type": attackType,
        "roles": List<dynamic>.from(roles.map((x) => x)),
      };
}

And here is where im getting the error, inside services folder:

class DotaServices {
  Future<List<String>?> getHeroes() async {
    var client = http.Client();
    var url = Uri.parse('https://api.opendota.com/api/heroes');
    var response = await client.get(url);
    if (response.statusCode == 200) {
      var json = response.body;
      return heroesFromJson(json);
    }
  }
}

The error is occuring in that line:

return heroesFromJson(json);

And the message that appears is:

A value of type 'List<Heroes>' can't be returned from the method 'getHeroes' because it has a return type of 'Future<List<String>?>'.

how to solve it? Im struggling real hard on this :/

3 Answers

Your method returns a list of heroes... so... you need to return a list of heroes:

Future<List<String>?> getHeroes() async {

needs to be

Future<List<Heroes>?> getHeroes() async {

heroesFromJson returns a list of heroes so getHeroes has to return a list of heroes:

Future<List<Heroes>?> getHeroes()

Also, your method heroesFromJson returns a List<Heroes> not nullable, but your method getHeroes() return a List<Heroe>? which is nullable.

You either can make your return from heroesFromJson a nullable list List<T>? or your return from getHeroes() a non-nullable list List

Be careful making your List nullable or non-nullable List<Hero>?, not your Hero List<Hero?>

It seems to me that such code should work more reliably.

return Hero.fromJsonList(json as List);

This small example (including function main) was generated with a very small script.

import 'dart:convert';

import 'package:http/http.dart' as http;

void main(List<String> args) async {
  final svc = DotaServices();
  final heroes = await svc.getHeroes();
  print('Heroes: ${heroes.length}');
}

class DotaServices {
  Future<List<Hero>> getHeroes() async {
    final client = http.Client();
    final url = Uri.parse('https://api.opendota.com/api/heroes');
    final response = await client.get(url);
    if (response.statusCode == 200) {
      final source = response.body;
      final json = jsonDecode(source);
      return Hero.fromJsonList(json as List);
    }

    throw StateError('Http error: ${response.statusCode}');
  }
}

class Hero {
  Hero(
      {required this.id,
      required this.name,
      required this.localizedName,
      required this.primaryAttr,
      required this.attackType,
      required this.roles});

  factory Hero.fromJson(Map json) {
    return Hero(
      id: json['id'] as int,
      name: json['name'] as String,
      localizedName: json['localized_name'] as String,
      primaryAttr: json['primary_attr'] as String,
      attackType: json['attack_type'] as String,
      roles: json['roles'] == null
          ? []
          : (json['roles'] as List).map((e) => e as String).toList(),
    );
  }

  final int id;

  final String name;

  final String localizedName;

  final String primaryAttr;

  final String attackType;

  final List<String> roles;

  static List<Hero> fromJsonList(List json) {
    return json.map((e) => Hero.fromJson(e as Map)).toList();
  }

  Map<String, dynamic> toJson() {
    return {
      'id': id,
      'name': name,
      'localized_name': localizedName,
      'primary_attr': primaryAttr,
      'attack_type': attackType,
      'roles': roles,
    };
  }

  static List<Map<String, dynamic>> toJsonList(List<Hero> list) {
    return list.map((e) => e.toJson()).toList();
  }
}

Using this codegen script you can generate the models and serializers.
It also generates a working example.

import 'dart:io';

import 'package:object_serializer/json_serializer_generator.dart';
import 'package:yaml/yaml.dart';

void main() {
  final classes = loadYaml(_classes) as Map;
  final g = JsonSerializerGenerator();
  final classesCode = g.generateClasses(classes);
  final values = {
    'classes': classesCode,
  };

  var source = g.render(_template, values);
  source = g.format(source);
  File('bin/stackoverflow.dart').writeAsStringSync(source);
}

const _classes = '''
Hero:
  fields:
    id: int
    name: String
    localizedName: {type: String, alias: localized_name}
    primaryAttr: {type: String, alias: primary_attr}
    attackType: {type: String, alias: attack_type}
    roles: List<String>
''';

const _template = r'''
import 'dart:convert';

import 'package:http/http.dart' as http;

void main(List<String> args) async {
  final svc = DotaServices();
  final heroes = await svc.getHeroes();
  print('Heroes: ${heroes.length}');
}

class DotaServices {
  Future<List<Hero>> getHeroes() async {
    final client = http.Client();
    final url = Uri.parse('https://api.opendota.com/api/heroes');
    final response = await client.get(url);
    if (response.statusCode == 200) {
      final source = response.body;
      final json = jsonDecode(source);
      return Hero.fromJsonList(json as List);
    }

    throw StateError('Http error: ${response.statusCode}');
  }
}

{{classes}}
''';

Related