2

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.

1

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.