Handle null value in Dart / Flutter - flutter

I have this User class and then a Firestore Document, that may or may not contain a photoURL or some other values. The problem is, that when I try to create an user I get this exception because some of the values are Null.
I've tried to handle it like this:
var photoURL = snapshot.data['photoURL'] ??= '';
but it seems it doesn't work.
Can anyone explain best practices handling Null values in Dart/Flutter respectively?
EDIT: I'm checking if snapshot.exists before and if I create the user omitting the values I know are Null, it creates it properly.
EDIT 2: I've found the problem appears when I try to handle empty List like this:
var favorites = snapshot.data['favorites'] ?? [''];

It seems I was initialized the value the wrong way when I converted it to Json.
I handle the empty Array like this
Map<String, dynamic> toJson() => {
'favorites' : favorites ?? '',
}
when it should be:
Map<String, dynamic> toJson() => {
'favorites' : favorites ?? [''],
}
So it was throwing when I tried to assign an empty Array to String.

Related

Flutter firebase: Bad state: field does not exist within the DocumentSnapshotPlatform

I'm getting this error:
Bad state: field does not exist within the DocumentSnapshotPlatform
with the following code:
static List<Report?> reportListFromSnapshot(QuerySnapshot snapshot) {
return snapshot.docs.map<Report?>((report) {
return Report(
type: report['type'],
reason: report['reason'],
reportId: report['id'],
chat:
(report['chat'] == null) ? null : Chat.chatFromMap(report['chat']),
stingray: Stingray.stingrayFromDynamic(report['stingray']),
reporterUser: User.fromDynamic(report['reporterUser']),
reportTime: report['reportTime'].toDate(),
);
}).toList();
}
Its failing on the first map,
type: report['type'],
and when i look at it in debug mode, it shows the data i am looking for:
As you can see from the screenshot, 'type' exists with a value of 'chat report'. Any idea why this is breaking?
Thanks!
You are supposed to call .data() on report
static List<Report> reportListFromSnapshot(QuerySnapshot snapshot) {
final List<Report> reports = [];
for (final doc in snapshot.docs) {
final report = doc.data() as Map<String, dynamic>?; // you missed this line.
if (report == null) continue;
reports.push(
Report(
type: report['type'] as String,
reason: report['reason'] as String,
reportId: report['id'] as String,
chat: (report['chat'] == null)
? null
: Chat.chatFromMap(report['chat']),
stingray: Stingray.stingrayFromDynamic(report['stingray']),
reporterUser: User.fromDynamic(report['reporterUser']),
reportTime: (report['reportTime'] as Timestamp).toDate(),
),
);
}
return reports;
}
// I returned List<Report> not List<Report?>
Check out this link on how to use withConverter so you do not have to manually convert models.
The problem turned out to be larger in scope than i thought. I was uploading a map object to firebase rather than just a document, and that map was attempting to be read, which is where the error occurred due to it only having one value in the document snapshot.
Maybe you have typo in your field which does not match with your model fields. So the idea is that, your fields in your model has to match with the fields in your firebase snapshot fields.
enter link description here

Firebase Realtime Database does not store Map with $type field

I have a class which I convert to a Map<String, dynamic> which contains a type field:
Map<String, dynamic> _$LibraryFolderToJson(LibraryFolder instance) =>
<String, dynamic>{
r'$type': instance.type,
'id': const GuidConverter().toJson(instance.id),
'parentId': const NullableGuidConverter().toJson(instance.parentId),
'relativePath': instance.relativePath,
};
Now I want to store this Map inside my Firebase Realtime Database, but it will not get stored. So I started to write some tests and found out, that the "$type" field is the problem. When I remove this line, everything is stored as it should.
Are there any restrictions from Firebase which prevents these type of fields?
Is there still a way to store this field?
This type of field comes from the .Net world (Newtonsoft.Json) and I adapted it into my flutter app. Is there a better field name for this?

Initialize with flexible keys in fromJson

I want to set Movie/TV information values that fetch form API, but what some JSON keys are different, but values types are same the difference is not so much to create another model for that, so I want to continue with main model class.
For example title is key for movie but for TV is name key in JSON, and some other keys that are different.
I've tried to solve it with checking, but it did not work:
final String? title;
factory Result.fromJson(Map<String, dynamic> json) => Result(
title: json["title"] == Null ? json["name"] : json["title"],
...
)
As I mentioned that work for Movie but for TV will not work, error that shows for that:
Unhandled Exception: type 'Null' is not a subtype of type 'String'
I also tried this method, but it didn't work for both parts as before:
title: json["name"] ?? json["title"]
How can I handle initialization with flexible keys?
Null is not string type, you can check with null
json["title"] == null ? json["name"] : json["title"],

Flutter Firebase Database, DataSnapshot? how to get values and keys, null safe

Using firebase_database: ^7.1.1
Before null safety i used to do like this to get the data from firebase database:
DataSnapshot dataSnapshot = await References.getReferenceYears().once();
Map<dynamic, dynamic> values = dataSnapshot.value;
values.forEach((key, value) {...}
Now when i try do like this, i get the error as bellow:
DataSnapshot? dataSnapshot = await References.getReferenceYears().once();
Map<dynamic, dynamic> values = dataSnapshot.value;
[ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception: type 'List<Object?>' is not a subtype of type 'Map<dynamic, dynamic>'
I think this means that the keys are not being returned in the dataSnapshot.value, how can i use this now to get the (key, value) as before?
If I print the snapshot.value I get like this, so looks like the keys are no longer present in the snapshot.value as before, i don't know where are the keys now:
[{name: 2016-2017}, {name: 2017-2018}, {name: 2018-2019}]
Thanks in advance.
You probably update Flutterfire cloud_firestore too, becouse as you can see from the error, in the newest versione, the data returned from the snapshot is an Object or a List<Object> and not a map.
You can solve like this:
snapshot.value as Map<String, dynamic>
EDIT:
I saw you where referring to firebase realtime database.
So the problem is not caused by flutter_fire.
The error tells you that you are trying to put a List into a Map<dynamic,dynamic>.
One solution could be:
Map<dynamic, dynamic> values = dataSnapshot.value as Map<dynamic, dynamic>;
Try this
DataSnapshot dataSnapshot = await References.getReferenceYears().once();
(dataSnapshot as Map<dynamic, dynamic>).forEach((key, value) {...}
It sounds like you have sequential, numeric keys under References.getReferenceYears(), which the SDK converts to an array. I don't think this changed in a recent SDK, as Firebase always tries to coerce such keys into an array.
If you don't want the values to be stored into an array, the easiest way to prevent that is to prefix the keys with a short non-numeric value, like:
"key_0": "value 0",
"key_1": "value 1",
"key_2": "value 2",
...
Use version 8.x.x for now as output type is dynamic instead of Object? in version 9.x.x
Map<dynamic, dynamic> mapData;
snapshot.values.forEach((key, value) {
mapData[key][value];
})

Get index of list of map from map

How do you get index of list of maps from a map. I tried to use indexOf which works but by the time the position of values are mixed up then it returns -1.
UPDATE: It actually doesn't work even in right order
List<Map<String, dynamic>> list = [{'id':1, 'name':'a'}, {'id':2, 'name':'b'}];
Map<String, dynamic> item = {'name':'a','id':1}; //<- position name and id are in different places
print(list.indexOf(item)); // so it return -1
The best way would be to get index of list where item contains same id ... if you know what I mean... How to do it?
You can use indexWhere instead indexOf.
Map<String, dynamic> item = {'name':'a','id':1};
print(list.indexWhere((i) => i['id'] == item['id'])); // prints 0
Map<String, dynamic> item = {'name':'b','id':2};
print(list.indexWhere((i) => i['id'] == item['id'])); // prints 1