Siva Perumal
Siva Perumal

Reputation: 497

type List<dynamic> is not a subtype of type 'Map<String, dynamic>'

I am trying to fetch data from an API. I got the error 'type List is not a subtype of Map. I am new to this and didn't understand where I am making mistake.

This is my fetch post function:

Future<Post> fetchPost() async {
  final response = await http.get('http://starlord.hackerearth.com/beercraft');

  if (response.statusCode == 200) {
    return Post.fromJson(json.decode(response.body));
  } else {
    throw Exception('Failed to load post');
  }
}

In my home screen I am using a FutureBuilder like this:

final Future<Post> post;
HomeScreen({Key key, this.post}) : super(key: key);

FutureBuilder<Post>(
          future: post,
          builder: (context, result) {
            if (result.hasData) {
              return ListView.builder(
                itemBuilder: (context, index){
                  return cartItemComponent(result.data.name, result.data.abv, result.data.ounces);
                },
              );
            } else if (result.hasError) {
              return Text("${result.error}");
            }

            // By default, show a loading spinner.
            return CircularProgressIndicator();
          },

How can I resolve this error? Thanks in advance.

Upvotes: 2

Views: 1345

Answers (1)

creativecreatorormaybenot
creativecreatorormaybenot

Reputation: 126564

The reason for this is that the API you are calling (http://starlord.hackerearth.com/beercraft) returns a list of JSON objects, but you are handling it as if there was only a single object.

You will need to rewrite your code to handle it as a list instead, which should work perfectly with your ListView:

Future<List<Post>> fetchPosts() async {
  final response = await http.get('http://starlord.hackerearth.com/beercraft');

  if (response.statusCode == 200) {
    return json.decode(response.body).map<Post>((item) => Post.fromJson(item)).toList();
  } else {
    throw Exception('Failed to load post');
  }
}

fetchPost needs to be updated to fetchPosts to return a Future<List<Post>>. Here you can simply map every item in the response list to a Post object using List.map.
The next step is to handle the returned Future as a list in your FutureBuilder like this:

final Future<List<Post>> posts;

FutureBuilder(
    future: posts,
    builder: (context, AsyncSnapshot<List<Post>> snapshot) {
      if (snapshot.hasData) {
        return ListView(
          children: snapshot.data.map<Widget>((post) {
            return cartItemComponent(post.name, post.abv, post.ounces);
          }).toList(),
        );
      } else if (snapshot.hasError) {
        return Text('${snapshot.error}');
      }

      // By default, show a loading spinner.
      return const CircularProgressIndicator();
    }
)

Notice that I simply passed the list as a list of Widget's to the children parameter of ListView because no builder is needed in this case.
I simply made use of the map method again to transform the Post's into Widget's using your cartItemComponent function.

Upvotes: 2

Related Questions