Unhandled Exception: type 'List<dynamic>' - flutter

I'm trying to recieve a list from a Sql Api. The catch is that i need to give an id with the query. the Widget.klant.klantId has the value i need. i know it has somthing to do with the as List<Machine> in accountpage.dart. Hope you can help me with this problem. thanks in advance.
The hole error:
accountpage.dart:
class Accountpage extends StatefulWidget {
const Accountpage(this.klant);
final Klant klant;
#override
_AccountpageState createState() => _AccountpageState();
}
class _AccountpageState extends State<Accountpage> {
_AccountpageState();
final ApiService api = ApiService();
late List<Machine> machineList;
#override initState(){
super.initState();
_getMachine();
machineList = [];
}
void _getMachine() async{
machineList = (await ApiService().getMoreMachine(widget.klant.klantId.toString())) as List<Machine>;
Future.delayed(const Duration(seconds: 1)).then((value) => setState(() {}));
}
#override
Widget build(BuildContext context) {
//Here starts the body
api_machine.dart:
Future<Machine> getMoreMachine(String klantId) async {
final response = await get(Uri.parse('$apiUrl/Select/$klantId'));
if (response.statusCode == 200) {
return Machine.fromJson(json.decode(response.body));
} else {
throw Exception('Failed to load a case');
}
}
MachineModel.dart:
List<Machine> welcomeFromJson(String str) => List<Machine>.from(json.decode(str).map((x) => Machine.fromJson(x)));
String welcomeToJson(List<Machine> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class Machine {
Machine({
this.serienummerId,
this.serienummer,
this.bouwjaar,
this.urenstand,
this.locatie,
this.klantId,
});
int? serienummerId;
String? serienummer;
String? bouwjaar;
String? urenstand;
String? locatie;
String? klantId;
factory Machine.fromJson(Map<String, dynamic> json) => Machine(
serienummerId: json["SerienummerId"],
serienummer: json["Serienummer"],
bouwjaar: json["Bouwjaar"],
urenstand: json["Urenstand"],
locatie: json["Locatie"],
klantId: json["KlantId"],
);
Map<String, dynamic> toJson() => {
"SerienummerId": serienummerId,
"Serienummer": serienummer,
"Bouwjaar": bouwjaar,
"Urenstand": urenstand,
"Locatie": locatie,
"KlantId": klantId,
};
}
json result
[
{
"SerienummerId": 1,
"Serienummer": "-----",
"Bouwjaar": "2020",
"Urenstand": "10",
"Locatie": "---",
"KlantId": "1"
},
{
"SerienummerId": 2,
"Serienummer": "-----",
"Bouwjaar": "1998",
"Urenstand": "5010",
"Locatie": "----",
"KlantId": "1"
}
]

You are parsing the result as if it's a single Machine while it in fact is a list of machines. Process it as a list and also use the correct return type accordingly. Like
Future<List<Machine>> getMoreMachine(String klantId) async {
final response = await get(Uri.parse('$apiUrl/Select/$klantId'));
if (response.statusCode == 200) {
return List<Machine>.from(json.decode(response.body).map((x) => Machine.fromJson(x)));
} else {
throw Exception('Failed to load a case');
}
}

the return type of the method is Machine:
Future<Machine> getMoreMachine(String klantId) async {
final response = await get(Uri.parse('$apiUrl/Select/$klantId'));
if (response.statusCode == 200) {
return Machine.fromJson(json.decode(response.body));
} else {
throw Exception('Failed to load a case');
}
}
and then you cast a Machine to List :
machineList = (await ApiService()
.getMoreMachine(widget.klant.klantId.toString())) as List<Machine>;
I don't know what the JSON looks like... but if there is only one machine you could for example add it to a list like this:
machineList.add((await ApiService()
.getMoreMachine(widget.klant.klantId.toString())));
Update
Try this:
Future<List<Machine>> getMoreMachine(String klantId) async {
final response = await get(Uri.parse('$apiUrl/Select/$klantId'));
if (response.statusCode == 200) {
final jsonMachines = Machine.fromJson(json.decode(response.body));
return jsonMachines.map((item) => Machine.fromJson(item)).toList();
} else {
throw Exception('Failed to load a case');
}
}

I think this is because of in getMoreMachine you used return type as Machine actually you are assigning that value as List so make that change like this :
Future<List<Machine>> getMoreMachine(String klantId) async {
final response = await get(Uri.parse('$apiUrl/Select/$klantId'));
if (response.statusCode == 200) {
return welcomeFromJson(response.body);
} else {
throw Exception('Failed to load a case');
}
}
might be other think is you can check your API response that is not returning List of machines.

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.

How to solve value of type 'Map<String, dynamic>', but got one of type 'List<dynamic>'

I want to get an image from an api and I get the error mentioned in the title.
class _ApiState extends State<Api> {
Future<CatData> fetchcat() async {
final response =
await http.get(Uri.parse('https://api.thecatapi.com/v1/images/search'));
// Appropriate action depending upon the
// server response
if (response.statusCode == 200) {
return CatData.fromJson(json.decode(response.body));
//return CatData.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
} else {
throw Exception('Failed to load album');
}
}
late Future<CatData> futureAlbum;
#override
void initState() {
super.initState();
futureAlbum = fetchcat();
}
#override
Widget build(BuildContext context) {
return FutureBuilder<CatData>(
future: fetchcat(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Image.network(snapshot.data!.imagen);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
return CircularProgressIndicator();
},
);
}
}
here the class model:
class CatData {
String imagen;
CatData({required this.imagen});
factory CatData.fromJson(Map<String, dynamic> json) {
return CatData(
imagen: json['url'],
);
}
}
If I get an answer please, I would like you to explain to me the reason for the problem. because I always get this kind of errors when I consume API's.
"receives one value but expects another"
https://api.thecatapi.com/v1/images/search
Well, json.decode(response.body) gives you back a List<dynamic>, but you declared the method fromJson to accept one argument of type Map<String, dynamic>, thus the incompatibility.
You can change the signature of the method fromJson and set it to List<dynamic>. Then you could access it with json[0].url, json[0]['url'] or {url} = json[0].
I tested the following code in https://dartpad.dev and works like a charm now.
import 'dart:convert';
import 'package:http/http.dart' as http;
Future<CatData> fetchcat() async {
final response =
await http.get(Uri.parse('https://api.thecatapi.com/v1/images/search'));
// Appropriate action depending upon the
// server response
if (response.statusCode == 200) {
return CatData.fromJson(json.decode(response.body));
//return CatData.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
} else {
throw Exception('Failed to load album');
}
}
class CatData {
String imagen;
CatData({required this.imagen});
factory CatData.fromJson(List<dynamic> json) {
return CatData(
imagen: json[0]['url']
);
}
}
void main() async {
CatData catData = await fetchcat();
print(catData.imagen);
}
You probably making mistake on casting. first make sure what kind of data you are retrieving means is it key-value pair { "url" : "www...." } or List [{"url" :"www...} , { "url": " www..."}]
if its key-value pairs then decode it as follows:
final decoded = json.decode(response.body) as Map<String, dynamic>;
final _catData = CataData.fromJson(decoded);
or if its list of urls then do it as follows:
final _decoded = json.decode(response.body) as List<dynamic>;
final _catsData = _decoded.map((e) => CatData.fromJson(e as Map<String, dynamic>)).toList();

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();
}

How do you turn a List <dynamic> into a List <Map> >>

I started to learn Flutter because I want to build an app which can handle API-Calls.
But now I'm frustrated because I want to make an infinite Load and don't get it to work.
The Problem is, that the method require an Future<List> but I dont know how to convert the response from the API into an List
Future<List<Map>> _getServerData(int length) async{
String api = data.url +length.toString();
final res=
await http.get("data.url");
if (res.statusCode == 200) {
List<dynamic> resp = jsonDecode(res.body);
return resp;
} else {
throw Exception('Failed to load DATA');
}
}
The whole class is out of an Tutorial from oodavid.
But in his tutorial he dont use an API
Future<List<Map>> _getExampleServerData(int length) {
return Future.delayed(Duration(seconds: 1), () {
return List<Map>.generate(length, (int index) {
return {
"body": WordPair.random().asPascalCase,
"avatar": 'https://api.adorable.io/avatars/60/${WordPair.random().asPascalCase}.png',
};
});
});
}
That was the how he solved it
Down below is the whole class
import 'dart:async';
import 'dart:convert';
import 'package:Kontra/pages/articel_list.dart';
import 'package:http/http.dart' as http;
import 'package:Kontra/api/url.dart' as data;
import 'package:Kontra/api/articelsResponse.dart';
/// Example data as it might be returned by an external service
/// ...this is often a `Map` representing `JSON` or a `FireStore` document
Future<List<Map>> _getServerData(int length) async{
String api = data.url +length.toString();
final res=
await http.get(data.url);
if (res.statusCode == 200) {
List<dynamic> resp = jsonDecode(res.body);
return resp;
} else {
throw Exception('Failed to load DATA');
}
}
/// PostModel has a constructor that can handle the `Map` data
/// ...from the server.
class PostModel {
String sId;
String title;
String text;
String author;
String pictures;
String link;
int postId;
String createdAt;
PostModel({this.title, this.text, this.pictures, this.link, this.postId});
factory PostModel.fromServerMap(Map<String, dynamic> json) {
return PostModel(
title: json['title'],
text: json['text'],
pictures: json['pictures'],
link: json['link'],
postId: json['postId']
);
}
}
/// PostsModel controls a `Stream` of posts and handles
/// ...refreshing data and loading more posts
class PostsModel {
int reload = 0;
Stream<List<PostModel>> stream;
bool hasMore;
bool _isLoading;
List<Map> _data;
StreamController<List<Map>> _controller;
PostsModel() {
_data = List<Map>();
_controller = StreamController<List<Map>>.broadcast();
_isLoading = false;
stream = _controller.stream.map((List<Map> postsData) {
return postsData.map((Map postData) {
return PostModel.fromServerMap(postData);
}).toList();
});
hasMore = true;
refresh();
}
Future<void> refresh() {
return loadMore(clearCachedData: true);
}
Future<void> loadMore({bool clearCachedData = false}) {
if (clearCachedData) {
_data = List<Map>();
hasMore = true;
}
if (_isLoading || !hasMore) {
return Future.value();
}
_isLoading = true;
return _getServerData(reload++).then((postsData) {
_isLoading = false;
_data.addAll(postsData);
hasMore = (_data.length < 30);
_controller.add(_data);
});
}
}
Thanks for your help guys
Try with
return List<Map>.from(resp.whereType<Map>());
Or
return resp.whereType<Map>().toList();
Or
return resp.cast<Map>();

Handling Errors with Dio/bloc Flutter

I need a good way to handle errors while I'm Using Dio requests.
Can I do it in one class and pass the dio request throw it ?
and it should return a response with the error .
I am posting my generalized network bloc here, which can be reused any number of time, any where. also, It uses dio using API repository , exceptional and error handling.
class NetworkBloc extends Bloc<NetworkEvent, NetworkState> {
NetworkBloc() : super(NetworkRequestInitial());
#override
Stream<NetworkState> mapEventToState(
NetworkEvent event,
) async* {
yield NetworkRequestInitiated();
if (event is NetworkCallEvent) {
RequestType requestType = event.requestType;
if (requestType == RequestType.GET) {
yield* fetchData(event);
} else if (requestType == RequestType.POST) {
yield* uploadDataAndStoreResult(event);
}
}
}
Stream<NetworkState> fetchData(NetworkCallEvent event) async* {
Response response;
try {
yield NetworkRequestLoading();
response =
await event.apiRepository.sendGetRequest(event.url, event.request);
if (response.statusCode == 200) {
yield NetworkRequestLoaded(response: response);
} else {
Map jsonResponse = jsonDecode(response.data);
yield NetworkRequestFailure(message: jsonResponse['message']);
}
} catch (e) {
yield NetworkRequestFailure(
message: NetworkUtils.getErrorMessageAccordingToError(e));
}
}
Stream<NetworkState> uploadDataAndStoreResult(NetworkCallEvent event) async* {
Response response;
try {
yield NetworkRequestLoading();
if (event.request != null) {
if (event.isHeadersNeeded) {
response = await event.apiRepository.sendPostRequestWithHeader(
event.url,
request: event.request,
);
} else {
response = await event.apiRepository.sendPostRequest(
event.url,
event.request,
);
}
} else {
response = await event.apiRepository
.sendPostRequestWithoutBodyParameters(event.url);
}
if (response.statusCode == 200) {
saveDataAccordingToCacheMechanism(event, response);
yield NetworkRequestLoaded(response: response);
} else {
Map jsonResponse = jsonDecode(response.data);
yield NetworkRequestFailure(message: jsonResponse['message']);
}
} catch (e) {
yield NetworkRequestFailure(
message: NetworkUtils.getErrorMessageAccordingToError(e));
}
}
void saveDataAccordingToCacheMechanism(
NetworkCallEvent event, Response response) async {
if (event.cacheMechanism == CacheMechanism.SharePreferences) {
Hive.box(ConstUtils.dbName)
.put(event.keyForSharedPreferences, response.data.toString());
} else if (event.cacheMechanism == CacheMechanism.Database) {}
}
}
I am also adding states and events to make it more easy to understand.
class NetworkCallEvent extends NetworkEvent {
final String request;
final dynamic url;
final RequestType requestType;
final CacheMechanism cacheMechanism;
final String keyForSharedPreferences;
final APIRepository apiRepository;
final bool isHeadersNeeded;
NetworkCallEvent(
{#required this.url,
this.request,
this.isHeadersNeeded = false,
#required this.requestType,
#required this.apiRepository,
#required this.cacheMechanism,
this.keyForSharedPreferences});
#override
List<Object> get props => [
this.url,
this.request,
this.requestType,
this.cacheMechanism,
this.keyForSharedPreferences,
this.apiRepository
];
}
Network_states:
class NetworkRequestInitial extends NetworkState {}
class NetworkRequestInitiated extends NetworkState {}
class NetworkRequestLoading extends NetworkState {}
class NetworkRequestLoaded extends NetworkState {
final dynamic response;
NetworkRequestLoaded({this.response});
#override
List<Object> get props => [this.response];
}
class NetworkRequestFailure extends NetworkState {
final String message;
NetworkRequestFailure({this.message});
#override
List<Object> get props => [this.message];
}
You can easily send request in JSON and get Response in dynamic, which you can convert to appropriate object using json.decode().