So, I have been making a post request to a REST API and I want to store the response data in the firebase cloud store collection.
What I have done so far:
I have created the model class for the response data and have written a function that will make this post-call.
I am not getting any such error but still, neither the response is getting printed in the console nor the data is being uploaded in the firebase.
Also, I have checked with almost all the StackOverflow questions that relate to my kind of problem.
Herewith I am attaching my code snippets:
Function:
//This function is only not getting called I don't know why.
final List<KycDetails> _kyc = [];
Dio dio = Dio();
TextEditingController aadhar = TextEditingController();
Future<List<KycDetails>> postData() async {
const String pathUrl = 'https://jsonplaceholder.typicode.com/posts';
dynamic data = {'title': aadhar.text, 'body': 'Flutter', 'userId': 1};
List<KycDetails> details = [];
var response = await dio.post(pathUrl,
data: data,
options: Options(
headers: {'Content-Type': 'application/json; charset=UTF-8'}));
if (response.statusCode == 200) {
print('ok');
var urjson = jsonDecode(response.data);
for (var jsondata in urjson) {
details.add(KycDetails.fromJson(jsondata));
}
}
return details;
}
Widget where I am calling the function and storing the data in firebase
InkWell(
hoverColor: Colors.red,
onTap: () async {
print('API CALLING');
await postData().then((value) {
setState(() {
_kyc.addAll(value);
});
print(value);
});
Map<String, String> data = {
"aadhar": aadhar.text,
"title": _kyc[0].title,
"userId": _kyc[0].userId.toString(),
};
FirebaseFirestore.instance.collection('kyc').add(data);
},
child: const Text('Submit'),
),
API response data:
{"title": "resume", "body": "Flutter", "userId": 1, "id": 101}
Model Class:
class KycDetails {
KycDetails({
required this.title,
required this.body,
required this.userId,
required this.id,
});
String title;
String body;
int userId;
int id;
factory KycDetails.fromJson(Map<String, dynamic> json) => KycDetails(
title: json["title"],
body: json["body"],
userId: json["userId"],
id: json["id"],
);
Map<String, dynamic> toJson() => {
"title": title,
"body": body,
"userId": userId,
"id": id,
};
}
I hope I have provided you with the necessary information
Am stuck on this problem for quite a few days Would appreciate it if anyone can solve my problem considering my code.
For starters, when you make a post request the success code you're looking for is 201 indicating that a resource has been successfully created.
So nothing in this code block will run.
if (response.statusCode == 200) {
print('ok');
var urjson = jsonDecode(response.data);
for (var jsondata in urjson) {
details.add(KycDetails.fromJson(jsondata));
}
}
response.data doesn't need jsonDecode here. It returns in the form of a map so you can cast it as such.
So this
var urjson = jsonDecode(response.data);
can be this
final urjson = response.data as Map<String, dynamic>;
As for this line
for (var jsondata in urjson) {
details.add(KycDetails.fromJson(jsondata));
}
The response is a single map, not a list. That single map is in the form of your KycDetails model so you don't need to loop through anything.
So you can create your object with your fromJson method.
final kycDetail = KycDetails.fromJson(urjson);
Then you can just do this to add a properly initiated KycDetails object to the list.
details.add(kycDetail);
If all you're trying to do is add a single object to Firebase then none of this in your onTap is necessary. Also trying to access the property at index 0 will not be the most recent addition to the list. You'd need to add the index of the last item in the list.
Map<String, String> data = {
"aadhar": aadhar.text,
"title": _kyc[0].title,
"userId": _kyc[0].userId.toString(),
};
FirebaseFirestore.instance.collection('kyc').add(data);
You can just add to Firebase from your postData function.
if (response.statusCode == 201) {
print('ok');
final urjson = response.data as Map<String, dynamic>;
final kycDetail = KycDetails.fromJson(urjson);
details.add(kycDetail);
FirebaseFirestore.instance.collection('kyc').add(kycDetail.toJson());
}
Related
I receive an error that has something to do with JSON receiver inside Flutter/Dart.
Had to share in a docs file since the full json response is pretty long. It had like 15 columns error log
Detail Class
class Detail {
String kodkursus;
String namakursus;
String kursusdescription;
Detail(
{required this.kodkursus,
required this.namakursus,
required this.kursusdescription});
factory Detail.fromJson(Map<String, dynamic> json) {
return Detail(
kodkursus: json['crs_code'] as String,
namakursus: json['crs_title_bm'] as String,
kursusdescription: json['crs_description_bm'] as String,
);
}
}
Code
Future<dynamic> generateDetailList() async {
var url = 'http://10.0.2.2:81/login_testing/kursus_display.php';
var data = {'usr_id': widget.username2};
var response = await http.post(url, body: json.encode(data));
var list = json.decode(json.encode(response.body));
List<Detail> _detail =
list.map<Detail>((json) => Detail.fromJson(json)).toList();
detailDataSource = DetailDataSource(_detail);
return _detail;
}
Return (full error log)
NoSuchMethodError (NoSuchMethodError: Class 'String' has no instance method 'map'...
I fairly new to this Flutter/Dart but I got the feeling it had something to do with the json, it just I cant get my head over it
Please check your API response because this error generates when there are difference in datatype.
this error says your app response it in String and you are accessing this as map so please check your API response or
try to replace this :
var list = json.decode(json.encode(response.body));
with :
var list = json.decode(response.body);
because json.encode method encodes all list data and that values datatype is String so it gives error.
Replace your function generateDetailList as such:
Future<List<Detail>?> generateDetailList() async {
Uri url = Uri.parse('http://10.0.2.2:81/login_testing/kursus_display.php');
Map<String, String> data = {'usr_id': 'widget.username2'};
http.Response response = await http.post(url, body: json.encode(data));
// var list = json.decode(json.encode(response.body));
var responseMap = await jsonDecode(response.body);
if (response.statusCode == 200) {
List<Detail> _details =
responseMap.map<Detail>((x) => Detail.fromJson(x)).toList();
return _details;
} else {
return null;
}
}
And try not to use var everywhere.
Not sure if what I'm trying to do is a foolish en-devour or ? In the app, I use rest API calls and I'm trying to implement API handling with Dio.
I have gone with an approach like so.
each API method has a generic function. ie: the POST method. goes like
Future<dynamic> postRequest({String? endPoint, dynamic decoder, data}) async {
// late String _fullURL = BaseOptions().baseUrl + endPoint!;
late int? _responseCode;
try {
final response = await _dio.post(
endPoint!, // APIEndpoints.postSignUp,
data: data, //jsonEncode(data),
);
//
// final String _responseString = response.data;
_responseCode = response.statusCode;
//
if (_responseCode == 200) {
final String _responseString = response.data;
final res = decoder(_responseString);
log("post success response ------------ ${res.toString()}");
return SuccessHandler().checkSuccess(res);
} else if (_responseCode == 201) {
log("post success no response ------------ ");
return HTTPResponse<dynamic>(
false,
null,
message: 'empty success',
code: _responseCode!,
//'Something went wrong! Please try again in a moment!',
);
} else {
log("post failed response ------------ ");
return null;
// return ErrorHandler().checkError(_responseCode, _responseString);
}
} on DioError catch (e) {
// print(e.message);
// throw Exception(e.message);
log("exception ------------ ${e.message}");
return HTTPResponse<dynamic>(
false,
null,
message: 'error: ${e.message}',
code: e.response!.statusCode!,
//'Something went wrong! Please try again in a moment!',
);
}
}
Where I try to pass the decoder to get the JSON response read out from the API response. Using a generated class like this
ValidateEmailModel validateEmailModelFromJson(String str) =>
ValidateEmailModel.fromJson(json.decode(str));
String validateEmailModelToJson(ValidateEmailModel data) =>
json.encode(data.toJson());
class ValidateEmailModel {
ValidateEmailModel({
this.email,
this.type,
this.code,
this.expire,
this.createdTime,
});
String? email;
String? type;
String? code;
dynamic expire;
dynamic createdTime;
factory ValidateEmailModel.fromJson(Map<String, dynamic> json) =>
ValidateEmailModel(
email: json["email"],
type: json["type"],
code: json["code"],
expire: DateTime.parse(json["expire"].toString()),
createdTime: DateTime.parse(json["createdTime"].toString()),
);
Map<String, dynamic> toJson() => {
"email": email,
"type": type,
"code": code,
"expire": expire!.toIso8601String(),
"createdTime": createdTime!.toIso8601String(),
};
}
In my method calls. i give the function its paramters like so
res = await APIHandler.instance.postRequest(
endPoint: APIEndpoints.postValidateCode,
decoder: validateEmailModelFromJson,
data: {
"code": val,
"email": _email,
},
)
And await the response to be passed to another common method SuccessHandler() so I can get the data object/s
class SuccessHandler {
dynamic successObject;
SuccessHandler({this.successObject});
Future<dynamic> checkSuccess(dynamic response) async {
return SuccessHandler(successObject: response);
}
}
so far when I try to debug the code. I do get the response.data from the Dio response. but gives me
Unhandled Exception: type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'String'
can I achieve what I need to and how can I update the code? Please help me with this or just I think I'm trying to reinvent the wheel here.
validateEmailModelFromJson what this function will do is take a string convert it into json and will parse it you data model but in you postRequest function you are already get a json response from api call so no need to pass a decoder to you function.
where you are returning SuccessHandler().checkSuccess(res); directly return the _responseString so it should be like this,
if (_responseCode == 200) {
final String _responseString = response.data;
log("post success response ------------
${_responseString}");
return SuccessHandler().checkSuccess(_responseString);
}
now try directly using ValidateEmailModel.fromJson on _responseString, you will get the data
I have made a separate class which is my model class and has a function that posts to DISCORD WEB HOOK. I am setting the map values to true from the FORM UI screen. The textformfield value gets POSTED while MAP gives an error as following, just the textfield value gets posted and works fine but MAP doesn't work. Am I writing bad JSON?
P.S it is a discord hook so "content" is neccessary
I/flutter (26346): {"content": ["Could not interpret \"['testing options', {'Spot': True, 'Red': True, 'Thich': False, 'Dry': True, 'Strech': False}]\" as string."]}
Here is the code for the class
import 'dart:convert';
import 'package:http/http.dart';
class DataPush {
static const String Spot = 'Spot';
static const String Red = 'Red';
static const String Thick = 'Thich';
static const String Dry = 'Dry';
static const String Strech = 'Strech';
String firstName = '';
Map<String, bool> passions = {
Spot: false,
Red: false,
Thick: false,
Dry: false,
Strech: false,
};
save() {
print("Saved");
}
makePostRequest() async {
final uri = Uri.parse(
'MY DISCORD WEBHOOK URL');
final header = {'Content-Type': 'application/json'};
Map<String, dynamic> body = {
"content": [firstName, passions]
};
String jsonBody = json.encode(body);
Response response = await post(
uri,
headers: header,
body: jsonBody,
);
int statusCode = response.statusCode;
String responseBody = response.body;
print(statusCode);
print(responseBody);
}
}
Remove this line and directly pass Map to post function.
String jsonBody = json.encode(body);
The Discord webhook documentation specifies the content field as a string, but you are sending an array as the value of that field - If you really want to send an array in the content field, you'll need to encode it as a json string itself first.
makePostRequest() async {
final uri = Uri.parse(
'MY DISCORD WEBHOOK URL');
final header = {'Content-Type': 'application/json'};
Map<String, dynamic> body = {
"content": json.encode([firstName, passions]) // Change this line
};
String jsonBody = json.encode(body);
Response response = await post(
uri,
headers: header,
body: jsonBody,
);
int statusCode = response.statusCode;
String responseBody = response.body;
print(statusCode);
print(responseBody);
}
i have a problem with "fromJson" in my program. I get my json data back from the server. But I can't save them in an array and return them to my screen.
static Future<List<Infos>?> getTasks() async {
List<Info> infos= <Info>[];
try{
final http.Response response = await http.get(
Uri.parse('example.com/test.php'),
headers: <String, String> {
'Content-Type': 'application/json; charset=UTF-8',
},
);
// I have here my json-data like
// [{...}, {...}, {...}, ...]
var jsonData = json.decode(response.body);
jsonData.forEach((item) {
infos.add(Info.fromJson(item));
});
// but after foreach I have no data and no error. I think that the foreach causes errors because
// the program does not continue at this point.
return Future<List<Info>>.value(infos);
} on Exception catch (e) {
print(e.toString());
}
}
My InfoModel.dart is like this:
class Info {
String userId;
String infoId;
...
Info({
required this.userId,
required this.infoId,
...
});
factory Info.fromJson(Map<String, dynamic> json) {
return Info(
userId: json["UserId"],
taskId: json["InfoId"],
...
);
}
}
This code works with sdk: ">=2.7.0 <3.0.0" btu not with sdk: '>=2.12.0 <3.0.0'.
Thanks
I had a similar issue, but after wrapping call to Object.fromJson(json) with try/catch I was able to see that types were wrong (i.e. "type 'Null' is not a subtype of type 'int'"). Perhaps this can help someone.
I'm new to Flutter, so I'm not sure how I am supposed to make an API call, coming from Java where I was using Retrofit.
JSON Response:
{
"total": 13,
"rows": [
{
"id": 1,
"name": "Name"
}
]
}
Model class:
class Category {
int total;
List<Rows> rows;
Category({this.total, this.rows});
Category.fromJson(Map<String, dynamic> json) {
total = json['total'];
if (json['rows'] != null) {
rows = new List<Rows>();
json['rows'].forEach((v) {
rows.add(new Rows.fromJson(v));
});
}
}
}
class Rows {
String name;
Rows({this.name});
Rows.fromJson(Map<String, dynamic> json) {
name = json['name'];
}
}
Main class
List<Rows> rows = [];
Future<List<Rows>> getData() async {
var response = await http.get(
Uri.encodeFull("http://192.168.0.10/api/v1/categories"),
headers: {
"Authorization": "API",
"Accept": "application/json"
}
);
var jsonData = json.decode(response.body);
}
I'm not sure how to approach I tried getting the object with Rows.fromJson(), but I only get 'Instance of Rows' and by calling name I get null.
The approach is correct but for list you should use list.map for deserializing lists.
Try this, I didn't tested it, I just wrote it looking at your examples
var response = await http.get(
Uri.encodeFull("http://192.168.0.10/api/v1/categories"),
headers: {
"Authorization": "API",
"Accept": "application/json"
}
);
List<Rows> rows = [];
Map map = json.decode(response.body);
List l = map["rows"];
rows = l.map((map) => Rows.fromJson(map)).toList();
You can use json_serializable and json_annotation and build_runner