Issue
I have this custom Data Model named "ServiceModel"
as below:
class ServiceModel {
int status;
String message;
List<dynamic> errors;
Data data;
ServiceModel({
required this.status,
required this.message,
required this.errors,
required this.data,
});
factory ServiceModel.fromJson(Map<String, dynamic> json) => ServiceModel(
status: json["status"],
message: json["message"],
errors: List<dynamic>.from(json["errors"].map((x) => x)),
data: Data.fromJson(json["data"]),
);
Map<String, dynamic> toJson() => {
"status": status,
"message": message,
"errors": List<dynamic>.from(errors.map((x) => x)),
"data": data.toJson(),
};
}
class Data {
Data({
required this.serviceInfo,
required this.payInfo,
});
ServiceIfo serviceInfo;
PayInfo payInfo;
factory Data.fromJson(Map<String, dynamic> json) => Data(
serviceInfo: ServiceIfo.fromJson(json["serviceIfo"]),
payInfo: PayInfo.fromJson(json["payInfo"]),
);
Map<String, dynamic> toJson() => {
"serviceInfo": serviceInfo.toJson(),
"payInfo": payInfo.toJson(),
};
}
And I want to get a response from my API with this function:
Future<ServiceModel> getServiceDetail() async {
var url = "${MyStrings.baseUrl}api/providers/profile/ServiceAPI";
var _pref = await SharedPreferences.getInstance();
var header = {
'token': _pref.getString('token'),
};
var response = await http.get(url + '?serviceId=59', headers: header);
print(response.body);
var model = ServiceModel.fromJson((json.decode(response.body)));
print("Model is : $model");
return model;
}
And Display it with a FutureBuilder
as below:
FutureBuilder<ServiceModel>(
future: _service.getServiceDetail(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
print('snapshot: ${snapshot.data}');
return Center(
child: LinearProgressIndicator(
color: Colors.red,
),
);
} else {
return AnimatedSwitcher(
duration: const Duration(seconds: 1),
child: (snapshot.connectionState == ConnectionState.waiting)
? Align(
alignment: Alignment.center,
child: Container(
child: CircularProgressIndicator(
color: parseColor("F8A387"),
),
))
: DetailedServicePager(data: snapshot.data!));
}
},
)
Everything seems smooth here but it doesn’t work!! Snapshot returns null and the loading indicator is displayed.
In fact It seems everything past the model line:
var model = ServiceModel.fromJson((json.decode(response.body)));
Doesn’t work!!
I have Proved it by placing print("Model is : $model");
between model definition
and return model
I added a sample of my postman response by request:
{
"status": 200,
"message": "Successful",
"errors": [],
"data": {
"serviceIfo": {
"serviceId": 59,
"customerId": 3,
"providerId": 4,
"parentTitle": [],
"title": "Cleaning",
"price": 90000.0,
"date": "2022/1/1",
"time": "00:45",
"address": "string",
"specialDescription": [
{
"question": "test",
"featureType": 0,
"choice": "test",
"answer": "test"
}
],
"description": "string",
"lat": 0.0,
"lng": 0.0,
"customerFullName": "FirstName LastName",
"custoemrPhoneNumber": "",
"serviceStatus": "Abort Search",
"endTime": "",
"endDate": "",
"serviceCategoryFeaturs": null
},
"payInfo": {
"isPaied": false,
"paymentAmount": 90000.0
}
}
}
Solution
So I finally figured it out! My main mistake was in the mapping function in the ServiceModel
.
The response
was fine. The response.body
had data and the Json.decode(response.body)
returned a reasonable value and then the snapchat
was NULL.
The only thing that remained uncheck between Json.decode
and snapchat
was ServiceModel.fromJson((json.decode(response.body)))
.
By removing every field of the ServiceModel, Data, ServiceInfo, PayInfo in turn; I managed to find the problem.
It was because I had defined a description
field and forgot to define it in my fromJson(Map<String, dynamic> json)
as well. So this little problem made the mapping go wrong because it was defined but it wasn’t getting any value and it returned null.
On the other hand it seemed flutter decided to return null as the whole answer than just the suspicious field!
Answered By – David Black
Answer Checked By – David Marino (FlutterFixes Volunteer)