Handle null values in JSON parsing in a Flutter app - flutter

I am fetching the details from database and then I am parsing the json value. Below is the code for http request.
Future <List> getData() async{
if(endofrecord == false){
try{
var body = { "uid" : dtguid, "deviceid": deviceid, "offset": offset};
var url = 'http://192.168.1.100:8080/get_recommended.php';
// Starting Web API Call.
var response = await http.post(url, body: json.encode(body)).timeout(Duration(seconds: 5),
onTimeout: (){
// throw Exception();
_showSnackBar(context,'Some issue with connectivity. Can not reached to server.',Colors.redAccent);
//or you can also
return null;
});
if(response.statusCode == 200){
final data = parsedataFromJson(response.body);
setState(() {
recommended = true;
_inProcess = false;
if(data.count == null){
count = 0;
}else{
offset = offset + 5;
print(offset);
count = data.count;
}
if(data.content.length > 0 && data.content[0].name != 'Empty'){
for (var i in data.content) {
lists.add(i);
}
}else{
nodata = 'No Record Found';
endofrecord = true;
_showSnackBar(context,nodata,Colors.redAccent);
}
});
print(lists.length);
}
}catch(e){
print("Exception Caught: $e");
_showSnackBar(context,'Some issue with connectivity. Could not connect to server.',Colors.redAccent);
}
return lists;
}else{
return null;
}
}
Here is the JSON parsing.
import 'dart:convert';
DatabyPrice databyPriceFromJson(String str) => DatabyPrice.fromJson(json.decode(str));
class DatabyPrice {
DatabyPrice({
this.count,
this.content,
this.success,
});
int count;
List<Content> content;
bool success;
factory DatabyPrice.fromJson(Map<String, dynamic> json) => DatabyPrice(
count: json["count"],
content: List<Content>.from(json["content"].map((x) => Content.fromJson(x))),
success: json["success"],
);
}
class Content {
Content({
this.name,
this.uid,
this.pic,
this.state,
this.country,
this.lastLogin,
this.tabout,
this.averageOrating,
this.pricing,
});
String name;
int uid;
String pic;
String state;
String country;
String tabout;
String lastLogin;
String averageOrating;
List<Pricing> pricing;
factory Content.fromJson(Map<String, dynamic> json) => Content(
name: json == null ? 'Empty' : json["name"],
uid: json == null ? 0 :json["uid"],
pic: json == null ? 'Empty' :json["pic"],
state: json == null ? 'Empty' :json["state"],
tabout: json == null ? 'Empty' :json["tabout"],
country: json == null ? 'Empty' :json["country"],
lastLogin: json == null ? 'Empty' : json["last_login"],
averageOrating: json == null ? '0' :json["average_orating"],
pricing: List<Pricing>.from(json["pricing"].map((x) => Pricing.fromJson(x))),
);
}
class Pricing {
Pricing({
this.uid,
this.price,
this.serviceType,
});
int uid;
int price;
String serviceType;
factory Pricing.fromJson(Map<String, dynamic> json) => Pricing(
uid: json == null ? 0 :json["uid"],
price: json == null ? 0 :json["price"],
serviceType: json == null ? 'Empty' :json["service_type"],
);
}
Above code is working fine when there are some records returning from database but if there is no data or end of record then it is not working. I am getting below error.
I/flutter ( 5255): Receiver: null
I/flutter ( 5255): Tried calling: []("pricing")
I/flutter ( 5255): Exception Caught: NoSuchMethodError: The method '[]' was called on null.
How can I handle this situation when http request is not returning the data?

For converting the JSON into a PODO, you must use something like
JSON to Dart
Once the model is generated then it would be easy for you to check the null elements coming from the backend.

Did you catch any error in the try{} catch{} block.If your experiencing no errors check your custom JSON converter.Try testing without your custom JSON parsers and use the normal converter which converts JSON into a Map.If it still not working make sure you've import
the dart:async module like this import dart:asyncdart.If it doesn't change anything try using the .then() and .catch() syntax .If not try checking your backend database they may be something wrong

Related

Package not returning data?

im currently having an issue using a package, the basic function it does it uploads and image and bring backs the url of the image, im not having issues uploading images (i printed the info) but whenever i do the return to bring back the information to my actual app it just brings back null.
The first code is where i call the function to upload the image and bring it back and also it eventually ataches it to another function, this part is found in the actual app.
File file, OnCreatePetEvent event, Emitter<PetState> emit) async {
PetsRepository repository = PetsRepository();
try {
ImageFileModel fileModel = await repository.putImage(file);
print(fileModel.toJson());
fileModel.toJson();
event.petModel.image = fileModel.data.imageKey;
event.petModel.thumbnail = fileModel.data.thumbnailKey;
repository.createPet(event.petModel);
} catch (e) {
throw e;
}
}
The second code is the function where i upload the image and eventually returns it, as previously said it does upload it and sends back data, this part is found in the package.
final apiProvider = Api.instance;
await apiProvider.postFile('upload_image', file, true).then((value) async {
dynamic json = jsonDecode(await value.stream.bytesToString());
print(json);
print(json['message']);
if (value.statusCode == 200) {
ImageFileModel filesURLs = ImageFileModel.fromJson(json);
print(filesURLs.toJson());
return filesURLs;
} else {
return json['message'];
}
}).catchError((e) {
print('error uploading file: ${e}');
});
}
ImageModel
//
// final imageFileUiModel = imageFileUiModelFromJson(jsonString);
import 'dart:convert';
ImageFileModel imageFileModelFromJson(String str) =>
ImageFileModel.fromJson(json.decode(str));
String imageFileModelToJson(ImageFileModel data) => json.encode(data.toJson());
class ImageFileModel {
ImageFileModel({
required this.message,
required this.data,
});
String message;
Data data;
factory ImageFileModel.fromJson(Map<String, dynamic> json) => ImageFileModel(
message: json["message"],
data: Data.fromJson(json["data"]),
);
Map<String, dynamic> toJson() => {
"message": message == null ? null : message,
"data": data == null ? null : data.toJson(),
};
}
class Data {
Data({
this.imageKey,
this.thumbnailKey,
});
String? imageKey;
String? thumbnailKey;
factory Data.fromJson(Map<String, dynamic> json) => Data(
imageKey: json["image_key"] == null ? null : json["image_key"],
thumbnailKey:
json["thumbnail_key"] == null ? null : json["thumbnail_key"],
);
Map<String, dynamic> toJson() => {
"image_key": imageKey == null ? null : imageKey,
"thumbnail_key": thumbnailKey == null ? null : thumbnailKey,
};
}
Console log
I/flutter (18011): success
I/flutter (18011): {message: success, data: {image_key: images/571221DTO.jpeg, thumbnail_key: images/53221fFX.jpeg}}
E/flutter (18011): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: type 'Null' is not a subtype of type 'ImageFileModel'```

fetcing data error :Unhandled Exception: NoSuchMethodError: The method '[]' was called on null

Hello everyone!
I am trying to fetch my product Data from Firebase server ,when i sign in i got this error:
Unhandled Exception: NoSuchMethodError: The method '[]' was called on null.
when I face this problem before i added these variable to the function below:
bool? _isFavorite =
favoriteData[key] != null || favoriteData[key] == 'true'
? true
: false;
but when i deleted favorite data from the server it gave me error again! it should take the "False"value when it null!
i think my error related to null safety but i cant fix it
i will share the Function which related to to this error and model and if there any solution I would be grateful for it ,Thank you!
Fetch Data Function:
Future<void> fetchAndSetProducts([bool filterByUser = false]) async {
final filterString =
filterByUser ? 'orderBy="creatorId"&equalTo="$userId"' : '';
var url =
'https://ecommerce-test-753ad-default-rtdb.firebaseio.com/products.json?auth=$authToken&$filterString';
try {
final response = await http.get(Uri.parse(url));
final extractedData = json.decode(response.body) as Map<String, dynamic>;
if (extractedData == {}) {
return;
}
url =
'https://ecommerce-test-753ad-default-rtdb.firebaseio.com/userFavorites/$userId.json?auth=$authToken';
final favoriteResponse = await http.get(Uri.parse(url));
final favoriteData = json.decode(favoriteResponse.body);
print(favoriteData);
print(extractedData);
final List<Product> loadedProduct = [];
extractedData.forEach((key, value) {
bool? _isFavorite =
favoriteData[key] != null || favoriteData[key] == 'true'
? true
: false;
loadedProduct.add(
Product(
id: key,
title: value['title'],
price: value['price'],
imageUrl: value['imageUrl'],
description: value['description'],
isFavorite: _isFavorite,
),
);
});
_productItems = loadedProduct;
notifyListeners();
} catch (e) {
rethrow;
}
}
product Model class:
class Product with ChangeNotifier {
final String id;
final String title;
final double price;
final String imageUrl;
final String description;
bool isFavorite;
Product({
required this.id,
required this.title,
required this.price,
required this.imageUrl,
required this.description,
this.isFavorite = false,
});
void _setFavValue(bool newValue) {
isFavorite = newValue;
notifyListeners();
}
Future<void> toggleFavoriteStatus(String authToken, String
userId) async {
final oldStatus = isFavorite;
isFavorite = !isFavorite;
notifyListeners();
final url =
'https://ecommerce-test-753ad-default-
rtdb.firebaseio.com/userFavorites/$userId/$id.json?
auth=$authToken';
try {
final response = await http.put(
Uri.parse(url),
body: json.encode(isFavorite),
);
if (response.statusCode >= 400) {
_setFavValue(oldStatus);
}
} catch (error) {
_setFavValue(oldStatus);
}
}
I also Added this line to Firebase rules:
"products":{
".indexOn":["creatorId"],
By checking favoriteData[key] for null is not enough, it is possible that favoriteData itself is null, try this:
bool? _isFavorite =
favoriteData != null && favoriteData[key] != null && favoriteData[key] == 'true'
? true
: false;
Try this:
final value = favoriteData == null ? false : favoriteData[key] != null || favoriteData[key] == 'true';
bool? _isFavorite = value;

A non-null String must be provided to a Text widget. 'data != null' and Error logging in

I am trying to perform user login with my flutter app but then I keep getting data != null error and that a Text Widget must have a non-null string.
Upon further debugging, I realized the response['message'] is printing a null value so I implement a condition to check if it's not null before proceeding but yet still it keeps giving me the same error.
When I use response['message'].toString(), it still gives the same error.
this is the full error being thrown 'data != null': A non-null String must be provided to a Text widget.
the issue seems to be from the response['message'] but I just can't seem to find ways to solve it
This is Auth controller class
class AuthController extends GetxController {
AuthService authService = AuthService();
ProjectApis projectApis = ProjectApis();
String name = '';
String email = '';
String password = '';
String confirmPassword = '';
var isPasswordHidden = true.obs;
Future loginUser(BuildContext context) async {
buildLoader(context, message: 'Loading...');
http.Response response = await authService.signInUser(
email,
password,
);
if (response.statusCode == 200) {
Map<String, dynamic> responseData = json.decode(response.body);
debugPrint(responseData.toString());
debugPrint(responseData['message']);
if (responseData["status"] == true) {
User user = User.fromJson(responseData);
UserPreferences().setUser(user);
Navigator.pop(context);
Get.offAll(() => BottomNavigation());
return;
} else {
Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(responseData['message']),
));
return;
}
} else {
Navigator.pop(context);
showErrorDialog(context, message: "Server Error");
return;
}
}
}
This is the sign in function
Future<http.Response> signInUser(
String email,
String password,
) async {
Map data = {
'email': email,
'password': password,
};
var body = json.encode(data);
var url = Uri.parse(projectApis.loginUrl);
var response = await client.post(
url,
body: body,
headers: projectApis.headers,
);
return response;
}
This is the User model class
User userFromJson(String str) => User.fromJson(json.decode(str));
String userToJson(User data) => json.encode(data.toJson());
class User {
User({
this.id,
this.name,
this.email,
this.password,
this.passwordConfirm,
this.token,
});
int? id;
String? name;
String? email;
String? password;
String? passwordConfirm;
String? token;
String applicationDirPath = "";
factory User.fromJson(Map<String, dynamic> json) => User(
id: json["id"],
name: json["name"],
email: json["email"],
password: json["password"],
passwordConfirm: json["passwordConfirm"],
token: json["token"],
);
Map<String, dynamic> toJson() => {
"id": id,
"name": name,
"email": email,
"password": password,
"passwordConfirm": passwordConfirm,
"token": token,
};
}
Use null-operator like here
response['message'] ?? ''
If left side was null the right side will assing
But you can use this just if you are sure this happen because of this line
read in medium
Text widget doesn't accept nullable String, and reading map can provide null value. You can provide default value on null case like
Text(myMap["key"]??"defaultValue")
And for perser you can do
if (responseData["status"] != null && responseData["status"]==true ) {
I think problem is with the Text(responseData['message']), line.
Dart can't be sure that me message key exist on responseData Map. So Text(responseData['message']), can be null which is bad for null safety.
Just do:
String message = responseData['message'] ?? '';
The ?? operator will return an empty string in case ResponseData['message'] is null.
Then replace in your Text widget:
Text(message),

Create copy from list of object and change new list without changing original one flutter

I have a list of object and I want to have copy of that and change new one without changing original one.
List<Comment> manageComment(List<Comment> incomingComments) {
List<Comment> finalArr = [];
var comments = List.from(incomingComments);
while (comments.isNotEmpty) {
var comment = comments.removeAt(0);
if (comment.parentId == null) {
finalArr.add(comment);
} else {
for (var i = 0; i < finalArr.length; i++) {
var el = finalArr[i];
if (el.commentId == comment.parentId) {
comment.replyTo = el.user;
el.children.add(comment);
break;
} else {
for (var j = 0; j < el.children.length; j++) {
var childEl = el.children[j];
if (childEl.commentId == comment.parentId) {
comment.replyTo = childEl.user;
el.children.add(comment);
break;
}
}
}
}
}
}
print(finalArr[0].children);
return finalArr;
}
Comment class:
class Comment {
String commentId;
User user;
User replyTo;
String text;
num date;
String parentId;
List<Comment> children;
Comment({
this.commentId,
this.user,
this.replyTo,
this.text,
this.date,
this.parentId,
this.children,
});
Comment copyWith({
String commentId,
User user,
User replyTo,
String text,
num date,
String parentId,
List<Comment> children,
}) {
return Comment(
commentId: commentId ?? this.commentId,
user: user ?? this.user,
replyTo: replyTo ?? this.replyTo,
text: text ?? this.text,
date: date ?? this.date,
parentId: parentId ?? this.parentId,
children: children ?? this.children,
);
}
Comment.fromJson(Map json)
: commentId = json['commentId'],
text = json['text'],
parentId = json['parentId'],
user = User.fromJson(json['user']),
children = [],
date = json['date'];
}
I try this, but it change original list, too.
How can I achieve that?
I found this solution and works:
In Comment class:
Comment.clone(Comment source)
: this.commentId = source.commentId,
this.user = source.user,
this.replyTo = source.replyTo,
this.text = source.text,
this.date = source.date,
this.parentId = source.parentId,
this.children = source.children.map((item) => Comment.clone(item)).toList();
and get copy with this:
var comments = incomingComments.map((e) => Comment.clone(e)).toList();
reference link
You can try toJson and fromJson, i was also facing same problem, a list which has isSelected for change radio value. I have to show this list in carousel slider so if i change index 1 list, other indexes list also got change i try everything List.from, List.unmodifiable, List.of, List.addAll and all other options only toJson and fromJson works.
ReturnReason returnReasons = await ApiProvider().getReturnReason();
ReturnReason rList = ReturnReason.fromJson(returnReasons.toJson());
My ReturnReason class is-
import 'dart:convert';
ReturnReason returnReasonFromJson(String str) => ReturnReason.fromJson(json.decode(str));
String returnReasonToJson(ReturnReason data) => json.encode(data.toJson());
class ReturnReason {
ReturnReason({
this.status,
this.message,
this.data,
});
int status;
String message;
Data data;
factory ReturnReason.fromJson(Map<String, dynamic> json) => ReturnReason(
status: json["status"] == null ? null : json["status"],
message: json["message"] == null ? null : json["message"],
data: json["data"] == null ? null : Data.fromJson(json["data"]),
);
Map<String, dynamic> toJson() => {
"status": status == null ? null : status,
"message": message == null ? null : message,
"data": data == null ? null : data.toJson(),
};
}
class Data {
Data({
this.returnReason,
});
List<Reason> returnReason;
factory Data.fromJson(Map<String, dynamic> json) => Data(
returnReason: json["return_reason"] == null ? null : List<Reason>.from(json["return_reason"].map((x) => Reason.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"return_reason": returnReason == null ? null : List<dynamic>.from(returnReason.map((x) => x.toJson())),
};
}
class Reason {
Reason({
this.name,
this.value,
this.isSelected,
});
String name;
String value;
bool isSelected;
factory Reason.fromJson(Map<String, dynamic> json) => Reason(
name: json["name"] == null ? null : json["name"],
value: json["value"] == null ? null : json["value"],
isSelected: false
);
Map<String, dynamic> toJson() => {
"name": name == null ? null : name,
"value": value == null ? null : value,
"isSelected": isSelected
};
}
you can try replacing
var comments = List.from(incomingComments);
with
List comments = List()..addAll(incomingComments)
and instead of List<Comment> finalArr = []; please use List<Comment> finalArr = List();

Flutter: How i can make List for messages?

I need to make a list of messages, I have this model
enum MessageType {sent, received}
class Messages {
MessageType status;
String contactName;
String message;
String time;
Messages({ this.status, this.message, this.contactName, this.time});
}
And this way to make a list
final response = await http.get(url);
if(response.statusCode == 200){
print(response.body);
var allMessages = (json.decode(response.body) as Map)['messages'] as Map<String, dynamic>;
var MessagesList = List<Messages>();
allMessages.forEach((String key, dynamic val){
var record = Messages(contactName: val['ownerName'], message: val['body'], time: '123', status: );
});
I have two questions.
How can I substitute the value 'received' in status?
2.how to set received or sent depending on which id?
If id = 1, then put 'received', if other then put 'sent'
You should do something like this.
Convert your JSON to a List of Message using a custom "fromJson" method and, during the conversion, set the MessageType as needed.
enum MessageType { sent, received }
class Message {
MessageType status;
String contactName;
String message;
String time;
Message({this.status, this.message, this.contactName, this.time});
factory Message.fromJson(Map<String, dynamic> json) => Message(
status: json["status"] == 1 ? MessageType.received : MessageType.sent,
contactName: json["contactName"],
message: json["message"],
time: json["time"],
);
}
Future<List<Message>> getMessage() async {
final response = await http.get(url);
if (response.statusCode == 200) {
print(response.body);
List<Message> allMessages = List<Message>.from(
json.decode(response.body).map((x) => Message.fromJson(x)));
return allMessages;
}
}
You should be able to just use an inline ternary operator like this: testCondition ? trueValue : falseValue
implemented in your code would look like this:
allMessages.forEach((String key, dynamic val){var record = Messages(
contactName: val['ownerName'],
message: val['body'],
time: '123',
status: id==1 ? MessageType.received : MessageType.sent);
});
Hope this helps!