I'm using the following example from docs.flutter.dev to get data from a json endpoint and then to display some text and images. https://docs.flutter.dev/cookbook/networking/fetch-data
I'm getting the following error below which I have attempted to resolve without success.
type 'List' is not a subtype of type 'Map<String, dynamic>'
I tried to resolve this myself by changing the following in the code:
return parsedJson.map((job) => Album.fromJson(job)).toList();
This returned a different error.
type 'List' is not a subtype of type 'FutureOr'
Seems this is a somewhat common error but I've yet to find a solution to this particular use case from the actual flutter example code.
Below is the code I'm working with.
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:http/http.dart' as http;
import '../models/image_model.dart';
Future<Album> fetchAlbum() async {
final response = await http
.get(Uri.parse('endpoint url would be here'));
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
final parsedJson = json.decode(response.body);
print(parsedJson);
return parsedJson.map((job) => Album.fromJson(job)).toList();
return Album.fromJson(parsedJson);
// return Album.fromJson(jsonDecode(response.body)); // Returning list not map
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load album');
}
}
class Album {
final String id;
final String name;
final String? description; // some data comes back null so we make this optional
final String imageurl;
const Album({
required this.id,
required this.name,
this.description,
required this.imageurl,
});
factory Album.fromJson(Map<String, dynamic> json) {
return Album(
id: json['id'],
name: json['name'],
description: json['description'],
imageurl: json['imageUrl'],
);
}
}
class ApiTestPage extends StatefulWidget {
const ApiTestPage({Key, key}) : super(key: key);
@override
_ApiTestPageState createState() => _ApiTestPageState();
}
class _ApiTestPageState extends State<ApiTestPage> {
late Future<Album> futureAlbum;
TextStyle style = const TextStyle(fontSize: 20.0);
@override
void initState() {
super.initState();
futureAlbum = fetchAlbum();
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: const Text("Go Back Home"),
backgroundColor: Colors.black,
leading: IconButton(
icon: const Icon(Icons.arrow_back_ios_new_rounded),
onPressed: () => Navigator.of(context).pushReplacementNamed('/login'),
),
),
body: Center(
child: GridView.extent(
primary: false,
padding: const EdgeInsets.all(16),
crossAxisSpacing: 10,
mainAxisSpacing: 10,
maxCrossAxisExtent: 200.0,
children: <Widget> [
FutureBuilder<Album>(
future: futureAlbum,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data!.name);
// Need to figure out how to show name and imageurl here still
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
// By default, show a loading spinner.
return const CircularProgressIndicator();
},
)
],
)),
);
}
}
CodePudding user response:
It seems to me that your parsedJson
is a Map<String, dynamic>, so try using List.from(parsedJson.map((job) => Album.fromJson(job)))
ou
List<Album>.from(parsedJson.map((job) => Album.fromJson(job)))
CodePudding user response:
The first error it fixed with the change you made
Because the fetchAlbum method it's future, it needs few seconds to return the List you want, so, you should add an await before the call of fetchAlbum call and to make the initState method async
Like this:
@override
void initState() async{
super.initState();
futureAlbum = await fetchAlbum();
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
}