How to solve this List<dynamic> type error in Flutter/Dart - flutter

I am very new to flutter and dart. Trying to return a list from this future that pulls data from an api but I keep getting this error. Someone here helped me solve a similar error by casting to a list because I was calling map function on an array that returns an iterable but in this case here I'm not sure what needs to be done.
type List<dynamic> is not a subtype of type FutrueOr<ListCity>>
Data is received like below:
{
"data": {
"127287": {
"Feature_int_id": "127287",
"Admin1_str_code": "US06",
"Country_str_code": "US",
"Feature_str_name": "Acampo",
"Feature_dec_lat": "38.194",
"Feature_dec_lon": "-121.25"
},
"116496": {
"Feature_int_id": "116496",
"Admin1_str_code": "US06",
"Country_str_code": "US",
"Feature_str_name": "Acton",
"Feature_dec_lat": "34.49",
"Feature_dec_lon": "-118.22"
},
"124284": {
"Feature_int_id": "124284",
"Admin1_str_code": "US06",
"Country_str_code": "US",
"Feature_str_name": "Adelanto",
"Feature_dec_lat": "34.665",
"Feature_dec_lon": "-117.512"
},
}
Below is the code for future:
Future<List<City>> fetchCitiesByProvince(provinceCode) async {
final response = await http.get(Uri.https('localhost/msd', 'api/cities/' + provinceCode));
final responseJson = json.decode(response.body);
final dataMap = responseJson['data'];
if (response.statusCode == 200) {
List citiesList = [];
for (var city in dataMap.keys) {
if (dataMap[city]['Admin1_str_code'] == provinceCode) {
citiesList.add(
{
'cityCode': dataMap[city]['Feature_int_id'],
'cityName': dataMap[city]['Feature_str_name']
}
);
}
}
return citiesList;
} else {
throw Exception('Failed to load cities');
}
}
City Class:
class City {
final String cityCode;
final String cityName;
City({#required this.cityCode, #required this.cityName});
factory City.fromJson(Map<String, dynamic> json) {
return City(
cityCode: json['Feature_int_id'],
cityName: json['Feature_str_name']
);
}
}

You need to return List<City> in your method. So, change the following code:
List citiesList = [];
to
List<City> citiesList = [];
--- UPDATE ---
You need to user your City constructor or factory to generate the item from json like this:
City.fromJson(dataMap[city]);
// Or
City(cityCode: dataMap[city]['Feature_int_id'],
cityName: dataMap[city]['Feature_str_name']
);
Here the updated sample code:
Future<List<City>> fetchCitiesByProvince(provinceCode) async {
final response = await http.get(Uri.https('localhost/msd', 'api/cities/' + provinceCode));
final responseJson = json.decode(response.body);
final dataMap = responseJson['data'];
List<City> citiesList = [];
if (response.statusCode == 200) {
for (var city in dataMap.keys) {
if (dataMap[city]['Admin1_str_code'] == provinceCode) {
citiesList.add(City.fromJson(dataMap[city]));
}
}
} else {
throw Exception('Failed to load cities');
// either throwing an error or return empty list.
}
return citiesList;
}

Related

How to fetch an array data from API and map it to the dart object?

I'm trying to use Flutter documentation to map an array data (comes from API) to the dart object. The documentation uses a single Json object, not an array. I have the following codes:
Json data:
[
{
"channelId" : 1
"channelTitle" : "Photos"
"channelImage" : pr01.jpg
"channelLastPost" : null
"lastUpdate" : null
"userRef" : 1
},
{
"channelId" : 2
"channelTitle" : "Science"
"channelImage" : pr02.jpg
"channelLastPost" : "For test ...."
"lastUpdate" : "2023-01-03"
"userRef" : 1
}
]
ChannelListModel.dart:
class ChannelListModel {
String creator;
String? image;
String title;
String lastPost;
String lastUpdate;
ChannelListModel(
{required this.creator,
required this.image,
required this.title,
required this.lastPost,
required this.lastUpdate});
factory ChannelListModel.fromJson(Map<String, dynamic> json) {
return ChannelListModel(
creator: json['userRef'],
image: json['channelImage'],
title: json['channelTitle'],
lastPost: json['channelLastPost'],
lastUpdate: json['lastUpdate']);
}
Map<String, dynamic> toJson() {
return {
"userRef" : creator,
"channelImage" : image,
"channelTitle" : title,
"channelLastPost" : lastPost,
"lastUpdate" : lastUpdate
};
}
}
HttpRequest.dart:
class HttpServices {
Future<List<ChannelListModel>> getChannelList() async {
var url = base.BaseURL.channelListUrl;
final response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
return List<ChannelListModel>.fromJson(jsonDecode(response.body)); //I have problem in this line
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load album');
}
}
}
ChannelPage.dart:
class _ChannelsState extends State<Channels> {
List<ChannelListModel> channels = [];
#override
void initState() {
super.initState();
channels = getChannelsFromHttp(); // A valid array object needs to be provided here.
}
getChannelsFromHttp()async{
var httpService = HttpServices();
var result = await httpService.getChannelList();
return result;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
itemCount: channels.length,
itemBuilder: (context, index) =>
ChannelCard(channelModel: channels[index]),
),
);
}
}
I want to classify my codes so I decided to provided different dart files for each part. How can I fix my code?
Instead of
returnList<ChannelListModel>.fromJson(jsonDecode(response.body));
Try this code,
List<ChannelListModel> channels = [];
final res = jsonDecode(response.body);
channels.addAll(List<ChannelListModel>.from(
(res).map((x) => ChannelListModel.fromJson(x))));
return channels;
Added based on comments
#override
void initState() {
super.initState();
getChannelsFromHttp();
}
getChannelsFromHttp()async{
var httpService = HttpServices();
var result = await httpService.getChannelList();
setState((){
channels = result;
});
}
Your fromJson factory returns single ChannelListModel.
You can't use List<ChannelListModel>.fromJson. Instead iterate through List and convert each json to ChannelListModel
class HttpServices {
Future<List<ChannelListModel>> getChannelList() async {
var url = base.BaseURL.channelListUrl;
final response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
//return List<ChannelListModel>.fromJson(jsonDecode(response.body));
final data = jsonDecode(response.body) as List<dynamic>;
return data.map((e) => ChannelListModel.fromJson(e as Map<String, dynamic>))
.toList();
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load album');
}
}
}
You can modify the ChannelListModel.fromJson method to handle a list of JSON objects instead of a single object. Here's one way to do it:
factory ChannelListModel.fromJson(List<dynamic> jsonList) {
return jsonList.map((json) => ChannelListModel(
creator: json['userRef'],
image: json['channelImage'],
title: json['channelTitle'],
lastPost: json['channelLastPost'],
lastUpdate: json['lastUpdate'])
).toList();
}
You can also use jsonDecode to convert the response body to a List and then use the above method to convert the list to ChannelListModel
final response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
final jsonData = jsonDecode(response.body);
return ChannelListModel.fromJson(jsonData);
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load album');
}
Also, you need to update the getChannelsFromHttp function to assign the result of httpService.getChannelList() to channels variable instead of calling the function again.
#override
void initState() {
super.initState();
getChannelsFromHttp();
}
getChannelsFromHttp() async {
var httpService = HttpServices();
channels = await httpService.getChannelList();
setState(() {});
}
This should solve the problem in your code.

Can't retrieve more than one info from api in Flutter

I'm trying to fetch both temperature and icon name from Openweathermap api. The problem occurs when I try to to fetch icon(or rather icon name).
It returns an error :
Unhandled Exception: type 'String' is not a subtype of type 'int' of 'index'
And I can't seem to find where the problem is. In other models I have other data types and it works fine, here it doesn't.
When I add icon to model, view returns null for temperature.
Here is code for weather model:
class WeatherData {
int? temp;
String? icon;
WeatherData({
this.temp,
this.icon
});
WeatherData.fromJson(dynamic json) {
var tempInKelvin = json["main"]["temp"];
temp = (tempInKelvin - 273.15).round();
icon = json["weather"]["icon"];
}
}
This is weather service:
class WeatherService {
final apiKey = "7e23369c183254302bda0471cc3f848c";
Future<WeatherData?> getWeatherForLocation(LocationData location) async {
WeatherData? weatherData;
var params = {
"lat": location.lat.toString(),
"lon": location.lon.toString(),
"city": location.city,
"appId": apiKey,
};
var url = Uri.http('api.openweathermap.org', '/data/2.5/weather', params);
Response response = await get(url);
if (response.statusCode == HttpStatus.ok) {
var jsonResponse = jsonDecode(response.body) as Map<String, dynamic>;
weatherData = WeatherData.fromJson(jsonResponse);
print("Request successful: $jsonResponse");
return weatherData;
} else {
print("Request failed with status: ${response.statusCode}");
return weatherData;
}
}
}
This is weather controller:
class WeatherController extends GetxController {
final WeatherService _weatherService = Get.find();
Rxn<LocationData> locationData = Rxn();
Rxn<WeatherData> weatherData = Rxn();
// RxString infoText = "...".obs;
String get address =>
"${locationData.value?.city},${locationData.value?.county}, ${locationData.value?.country}";
String get temperature => "${weatherData.value?.temp}";
// String get icon => "${weatherData.value?.icon}";
#override
void onInit() async {
super.onInit();
await getCurrentLocation();
await getTemperatureForCurrentLocation();
await getWeatherIcon();
}
getCurrentLocation() async {
LocationData? location = await _weatherService.getCurrentLocation();
print(location?.city);
locationData.value = location;
}
getTemperatureForCurrentLocation() async {
if (locationData.value != null) {
weatherData.value=
await _weatherService.getWeatherForLocation(locationData.value!);
// _getInfoText(weatherData.value?.temp);
}
}
}
Looking at their API docs, it seems that weather contains an array of objects (as a result, you need an integer index to figure out which one you want).
https://openweathermap.org/current
To fix it, you can simply opt to always use the first object in the weather array and take the icon/description from that:
"weather": [
{
"id": 800,
"main": "Clear",
"description": "clear sky",
"icon": "01d"
}
],
Which is:
icon = json["weather"][0]["icon"];
Can you please check this:
var tempInKelvin = double.parse(json["main"]["temp"]);
or this if your number is integer:
var tempInKelvin = int.parse(json["main"]["temp"]);

Get Expected a value of type 'String', but got one of type 'Null' error when fetching some movie info from the MovieDB api

I tried a tutorial to create a movie application. It works fine to get popular movies but with the same code to get upcoming movies I got this error and UI doesn't show anything. The error is :
Expected a value of type 'String', but got one of type 'Null'
here is where error happens:
Future<List<Movie>> getPopularMovies({required int page}) async {
//we are going to get Response
Response _response =
await _http.request('/movie/popular', query: {'page': page});
if (_response.statusCode == 200) {
Map<String?, dynamic> _data = _response.data;
List<Movie> _movies = _data['results'].map<Movie>((_movieData) {
return Movie.fromJson(_movieData);
}).toList();
return _movies;
} else {
throw Exception('Couldn\'t get the popular movies');
}
}
Future<List<Movie>> getUpcomingMovies({required int page}) async {
//we are going to get Response
Response _response =
await _http.request('/movie/upcoming', query: {'page': page});
if (_response.statusCode == 200) {
Map<String?, dynamic> _data = _response.data;
List<Movie> _movies = _data['results'].map<Movie>((_movieData) {
return Movie.fromJson(_movieData);
}).toList();
return _movies;
} else {
throw Exception('Couldn\'t get the popular movies');
}
}
I tried jsonDecode(_response.data) but got the this error: Expected a value of type 'String', but got one of type '_JsonMap'
Maybe some particular field in your model class may receive null value from response...that's why flutter indicates error....
To avoid this you have to make the field nullable using (?)...
Example:
int? rank;
Inside constructor remove required keyword for that field.
Now it will accepts null values...
Can solve your problem
Future<List<Movie>> getUpcomingMovies({required int page}) async {
List<Movie> list = [];
Response _response = http
.request('/movie/upcoming', query: {'page': page})
.then((_response) {
setState(() {
var list = json.decode(_response.body);
//categories = list.map((category) => Category.fromJson(category)).toList();
if (list is List) {
Map<String?, dynamic> _data = _response.data;
List<Movie> _movies = _data['results'].map<Movie>((_movieData) {
return Movie.fromJson(_movieData);
}).toList();
return _movies;
}
else {
print('response is not list');
}
});
}
);
return list;
}

how to return json List using flutter

i want to return json object list but i dont know how
i'm using the sample doc from flutter the
here is my code
Future<Album> fetchAlbum() async {
final response =
await http.get('https://vpic.nhtsa.dot.gov/api/vehicles/getmodelsformake/honda?format=json');
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
return Album.fromJson(jsonDecode(response.body));
} 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 userId;
final List <String> Cm;
Album({this.userId, this.Cm});
factory Album.fromJson(Map<String, dynamic> json) {
return Album(
userId: json['Results'][0]['Make_Name'],
Cm: for( var i = 0 ; i < json['Count']; i++ ) {
Cm.add(json['Results'][i]['Make_Name']);
}
);
}
}
the error in Cm: for... line
In your code snippet you did not created a class to refer Results list. Try bellow code snippet.
import 'dart:convert';
import 'package:http/http.dart' as http;
Future<Album> fetchAlbum() async {
final response = await http.get(
'https://vpic.nhtsa.dot.gov/api/vehicles/getmodelsformake/honda?format=json');
if (response.statusCode == 200) {
return Album.fromJson(jsonDecode(response.body));
} else {
throw Exception('Failed to load album');
}
}
class Album {
int count;
String message;
String searchCriteria;
List<Results> results;
Album({this.count, this.message, this.searchCriteria, this.results});
Album.fromJson(Map<String, dynamic> json) {
count = json['Count'];
message = json['Message'];
searchCriteria = json['SearchCriteria'];
if (json['Results'] != null) {
results = new List<Results>();
json['Results'].forEach((v) {
results.add(new Results.fromJson(v));
});
}
}
}
class Results {
int makeID;
String makeName;
int modelID;
String modelName;
Results({this.makeID, this.makeName, this.modelID, this.modelName});
Results.fromJson(Map<String, dynamic> json) {
makeID = json['Make_ID'];
makeName = json['Make_Name'];
modelID = json['Model_ID'];
modelName = json['Model_Name'];
}
}
As the for-loop is not returning the list to the cm field, you may try using .map to do the mapping and return it.
Cm: json['Results'].map((e)=>e['Make_Name']).toList()
First off, Flutter is a Framework for Dart language, so you don't need Flutter to run that code. Run code below on console:
import 'dart:convert';
import 'package:http/http.dart' as http;
class NetService {
static Future fetchJsonData(String url) {
return
http.get(url)
.then((response) => response?.statusCode == 200 ? jsonDecode(response.body) : null)
.catchError((err) => print(err));
}
static Future<void> fetchCarModels() {
return
fetchJsonData('https://vpic.nhtsa.dot.gov/api/vehicles/getmodelsformake/honda?format=json')
.then((response) {
if (response != null) {
final Map<String, dynamic> data = response;
print('''
Count : ${data["Count"]}
Message : ${data["Message"]}
Search Criteria : ${data["SearchCriteria"]}
Models :
${(data["Results"] as List)?.fold<String>("", (m, e) => m + (e as Map<String, dynamic>)["Model_Name"] + ", ")}
'''
);
}
})
.catchError((err) => print(err));
}
}
void main(List<String> arguments) async {
await NetService.fetchCarModels();
}

Type Future<dynamic> is not a subtype of type FutureOr<List<Status>>

I need your help..
I have a list with CheckboxListTile. If I checked or unchecked a item, the list reload. So I tried it with an AsyncMemoizer.
Now the following error is returned: 'Future<dynamic> is not a subtype of type Future<List<Status>>'
Maybe you have other ideas as I can solve the problem with the list differently
final AsyncMemoizer _memoizer = AsyncMemoizer();
Future<List<Status>> fetchPost(){
return _memoizer.runOnce(() async {
final response =
await http.get(URL);
if (response.statusCode == 200) {
List<Status> tmp = Status.fromJsonList(json.decode(response.body));
return tmp;
} else {
throw Exception('Failed to load post');
}
});
}
static List<Status> fromJsonList(List<dynamic> json) {
List<Status> ret = new List<Status>();
for (int i=0;i<json.length;i++){
Status itm = Status(
id: json[i]['I'],
geschlecht: json[i]['G'],
adresse: json[i]['A'],
name: json[i]['AZ'],
//Checkbox
isSelected : isValueChange(i)
);
ret.add(itm);
}
return ret;
}
You need to tell your AsyncMemoizer what type to expect:
final AsyncMemoizer<List<Status>> _memoizer = AsyncMemoizer<List<Status>>();