AnswerBun.com

'List' is not a subtype of type 'Map'

Stack Overflow Asked by Uchenna Ndukwe on January 3, 2022

I am trying to fetch a quote from the an api https://type.fit/api/quotes but it’s not showing and its show me the this error: type 'List<dynamic>' is not a subtype of type 'Map<String, dynamic>'

This is the model class for the json:

class Autogenerated {
  String text;
  String author;

  Autogenerated({this.text, this.author});

  factory Autogenerated.fromJson(Map<String, dynamic> json) {
    return Autogenerated(
        text: json['text'],
        author: json['author'],
    );
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['text'] = this.text;
    data['author'] = this.author;
    return data;
  }
}

Now this I use the called this import 'package:http/http.dart' as http;
and now I used the http.get to call the api like this:

    final response =
  await http.get('https://type.fit/api/quotes');

here is the full code of it…

import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:quotes_season/modal.dart';
import 'package:http/http.dart' as http;

class Quote extends StatefulWidget {
  @override
  _QuoteState createState() => _QuoteState();
}

Future<Autogenerated> fetchAlbum() async {
  final response =
  await http.get('https://type.fit/api/quotes');

  if (response.statusCode == 200) {
    // If the server did return a 200 OK response,
    // then parse the JSON.
    return Autogenerated.fromJson(json.decode(response.body));
  } else {
    // If the server did not return a 200 OK response,
    // then throw an exception.
    throw Exception('Failed to load album');
  }
}

class _QuoteState extends State<Quote> {
  Future<Autogenerated> futureAutogenerated;

  @override
  void initState() {
    super.initState();
    futureAutogenerated = fetchAlbum();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: FutureBuilder(
          future: fetchAlbum(),
          builder: (context, snapshot) {
            if(snapshot.hasData){
              return Center(child: Text(snapshot.data.title));
            }else if(snapshot.hasError){
              return Center(child: Text("${snapshot.error}"));
            }

            return CircularProgressIndicator();
          }),
    );
  }
}

2 Answers

The site that you posted returns a List of what you modelled in your code as Autogenerated. Based on the rest of your code it seems you only want one of these Autogenerated objects, so you can just say to use the first index in the List that you retrieve.

Future<Autogenerated> fetchAlbum() async {
  final response =
  await http.get('https://type.fit/api/quotes');

  if (response.statusCode == 200) {
    // If the server did return a 200 OK response,
    // then parse the JSON.
    List parsedJson = json.decode(response.body);
    return parsedJson.isNotEmpty ? Autogenerated.fromJson(parsedJson[0]) : null;
  } else {
    // If the server did not return a 200 OK response,
    // then throw an exception.
    throw Exception('Failed to load album');
  }
}

Alternatively, if you want you want to display all of quotes you can parse that and return a list of Autogenerated, but this would involve changing more code in displaying all of the quotes.

Future<List<Autogenerated>> fetchAlbum() async {
  final response =
  await http.get('https://type.fit/api/quotes');

  if (response.statusCode == 200) {
    // If the server did return a 200 OK response,
    List jsonList = json.decode(response.body);
    List list = jsonList.map((elem) => Autogenerated.fromJson(elem)).toList();
    return list;
  }
  ...
}
class _QuoteState extends State<Quote> {
  Future<List<Autogenerated>> futureAutogenerated;

  @override
  void initState() {
    super.initState();
    futureAutogenerated = fetchAlbum();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: FutureBuilder(
          future: futureAutogenerated,
          builder: (context, snapshot) {
            if(snapshot.hasData) {
              return ListView.builder(itemCount: snapshot.data.length, itemBuilder: (context, index) => Text("${snapshot.data[index].text}, ${snapshot.data[index].author}"));
            }else if(snapshot.hasError) {
              return Center(child: Text("${snapshot.error}"));
            }

            return CircularProgressIndicator();
          }),
    );
  }
}

Full Working test code example - this should be used only as a proof of concept, you will need to implement this into your existing code:

import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

void main() {
  runApp(MaterialApp(home:Quote()));
}

class Quote extends StatefulWidget {
  @override
  _QuoteState createState() => _QuoteState();
}

Future<List<Autogenerated>> fetchAlbum() async {
  final response =
  await http.get('https://type.fit/api/quotes');

  if (response.statusCode == 200) {
    // If the server did return a 200 OK response,
    List jsonList = json.decode(response.body);
    List list = jsonList.map((elem) => Autogenerated.fromJson(elem)).toList();
    return list;
  }
  else {
    // If the server did not return a 200 OK response,
    // then throw an exception.
    throw Exception('Failed to load album');
  }
}

class _QuoteState extends State<Quote> {
  Future<List<Autogenerated>> futureAutogenerated;

  @override
  void initState() {
    super.initState();
    futureAutogenerated = fetchAlbum();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: FutureBuilder(
          future: futureAutogenerated,
          builder: (context, snapshot) {
            if(snapshot.hasData) {
              return ListView.builder(itemCount: snapshot.data.length, itemBuilder: (context, index) => Text("${snapshot.data[index].text}, ${snapshot.data[index].author}"));
            }else if(snapshot.hasError) {
              return Center(child: Text("${snapshot.error}"));
            }

            return CircularProgressIndicator();
          }),
    );
  }
}

class Autogenerated {
  String text;
  String author;

  Autogenerated({this.text, this.author});

  factory Autogenerated.fromJson(Map<String, dynamic> json) {
    return Autogenerated(
        text: json['text'],
        author: json['author'],
    );
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['text'] = this.text;
    data['author'] = this.author;
    return data;
  }
}

Answered by Christopher Moore on January 3, 2022

You are trying to fetch JSON list from the API endpoint, but your parsing code is parsing single JSON object.

Your method has to change to return list of objects:

Future<List<Autogenerated>> fetchAlbum() async {
  final response =
  await http.get('https://type.fit/api/quotes');

  if (response.statusCode == 200) {
    // If the server did return a 200 OK response,
    List jsonList = json.decode(response.body)
    List list = List.generate(jsonList.length, (i) => Autogenerated.fromJson(jsonList[i]));
    return list;
  } else {
    // If the server did not return a 200 OK response,
    // then throw an exception.
    throw Exception('Failed to load album');
  }
}

After this change of the API call, your state class should look like this:

class _QuoteState extends State<Quote> {
  Future<List<Autogenerated>> futureAutogenerated;

  @override
  void initState() {
    super.initState();
    futureAutogenerated = fetchAlbum();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: FutureBuilder(
          future: futureAutogenerated,
          builder: (context, snapshot) {
            if(snapshot.hasData){
              List<Autogenerated> list = snapshot.data;
              Autogenerated firstItem = list[0];
              return Center(child: Text(firstItem.text));
            }else if(snapshot.hasError){
              return Center(child: Text("${snapshot.error}"));
            }

            return CircularProgressIndicator();
          }),
    );
  }
}

If your goal is to create a list of elements, and not a single element, you would need to modify widget being used:

return Center(child: Text(firstItem.text));

and do something like this instead:

List<Autogenerated> list = snapshot.data;
return ListView.builder(
  itemCount: list.length,
  itemBuilder: (context, index) => Text(list[index]),
);

Answered by Aleksandar on January 3, 2022

Add your own answers!

Related Questions

Removing duplicates while sorting numbers inside a String in java

5  Asked on November 30, 2020 by swetha-haridoss

       

pointers cant read the correct elements in array

2  Asked on November 29, 2020 by jabou

   

pandas does not load the sub packages properly

0  Asked on November 29, 2020 by realbro

   

Webpack: how to copy html files into build folder?

2  Asked on November 29, 2020 by wai-yan-hein

   

Save string to file without converting newlines using Python

1  Asked on November 29, 2020 by kleiton-kurti

       

ngx-mask Do not allow negative value for the currency input

2  Asked on November 28, 2020 by ashot-aleqsanyan

   

Unity: Add extra time to slider

1  Asked on November 28, 2020 by christopher-madsen

     

Python cannot access list

3  Asked on November 28, 2020 by forge-mods

   

Get property name and value only if there is a value

1  Asked on November 28, 2020 by jimenemex

   

Count percentage of upper case words

1  Asked on November 28, 2020 by user13623188

   

Ask a Question

Get help from others!

© 2023 AnswerBun.com. All rights reserved. Sites we Love: PCI Database, MenuIva, UKBizDB, Menu Kuliner, Sharing RPP, SolveDir