TransWikia.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!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP