How to fill Map from DocumentSnapshot in Using Firestore in Flutter? - flutter

I am trying to fill up a Map from DocumentSnapshot in a Flutter.
but I'm having a syntax error within the code.
This is the code I'm trying to work with:
Future<Map<String, dynamic>> particularItem(String productId) async {
DocumentSnapshot prodRef = await _productReference.doc(productId).get();
Map<String, dynamic> itemDetail = new Map();
itemDetail['image'] = await getProductsImage(prodRef.data()['imageId'][0]);
itemDetail['color'] = prodRef.data()['color'];
itemDetail['size'] = prodRef.data()['size'];
itemDetail['price'] = prodRef.data()['price'];
itemDetail['name'] = prodRef.data()['name'];
itemDetail['productId'] = productId;
return itemDetail;
}
And this is the error I'm getting in the below snippet:
The operator '[]' isn't defined for the type 'Object'. Try defining the operator '[]'.
prodRef.data()['imageId']
prodRef.data()['color']
prodRef.data()['size']
prodRef.data()['price']
prodRef.data()['name']

You should cast prodRef.data() before use it. In dart object can't not use the operator []
final data = prodRef.data() as Map<String, dynamic>;
Then you can get property from data variable.
For more, you can read here Firebase documentation

Related

Flutter 3.3.4 - The operator '[]' isn't defined for the class 'Object'

I just upgraded to Flutter 3.3.4 and now get this error:
Error: The operator '[]' isn't defined for the class 'Object'.
- 'Object' is from 'dart:core'.
Try correcting the operator to an existing operator, or defining a '[]' operator.
: file.data()!['photo'];
Code looks like this:
Future<String?> getUsersPhoto(String uid) async {
final reference = FirebaseFirestore.instance.doc('users/$uid');
DocumentSnapshot doc = await reference.get();
var result = doc.data()!['photo'];
return result;
}
And a more complex function like this:
Stream<T> findUser<T> ({required String uid, required T builder(Map<String, dynamic>? data, String documentID)}) {
final ref = FirebaseFirestore.instance.doc('users/$uid');
final Stream<DocumentSnapshot> snapshots = ref.snapshots();
return snapshots.map((snapshot) => builder(snapshot.data(), snapshot.id));
}
Any idea how to progress from here?
Convert doc.data() to as Map<String, dynamic>.
Map<String, dynamic> data = doc.data()! as Map<String, dynamic>;
return data['photo']

Flutter - _CastError (type 'List<dynamic>' is not a subtype of type 'List<Map<String, dynamic>>' in type cast)

I stored a list of Map data in firestore database. But when I tried to fetch the data to a variable of type List<Map<String, dynamic>>, it shows the error "_CastError (type 'List' is not a subtype of type 'List<Map<String, dynamic>>' in type cast)".
Here is the code,
// code to fetch objects list from database
Future<List<Map<String, dynamic>>> getUserObjects() async {
Map<String, dynamic> userInfo = await getUserInfo();
List<Map<String, dynamic>> userObjects = userInfo['objects'] as List<Map<String, dynamic>>;
List<Map<String, dynamic>> result = [];
for(var i=0; i<userObjects.length; i++) {
var docInfo = await DatabaseMethods().getDocumentInfo(findCollectionName(userObjects[i]['objectType']), userObjects[i]['docId']) as Map<String, dynamic>;
result.add({'icon': findIcon(userObjects[i]['objectType']), 'objectName' : docInfo['objectName'], 'docId' : userObjects[i]['docId']});
}
return result;
}
Future<Map<String, dynamic>> getUserInfo() async {
var user = await FirebaseAuthentication().getCurrentUser();
Map<String, dynamic> userInfo = await DatabaseMethods().getuserFromDB(user.uid);
if(userInfo['objects'] == null) {
userInfo.addAll({'objects' : []});
}
return userInfo;
}
final FirebaseAuth auth = FirebaseAuth.instance;
Future<User> getCurrentUser() async {
return auth.currentUser!;
}
// code to add an object to object list in collection 'user'
Future<void> updateUserObjectList(Map<String, dynamic> object) async {
var user = await FirebaseAuthentication().getCurrentUser();
var userId = user.uid;
await FirebaseFirestore.instance.collection('users').doc(userId)
.update({'objects' : FieldValue.arrayUnion([object])});
}
The data stored in the database will look like the below image with the following changes,
replace 'quotes' with 'objects'
replace 'auther' with 'objectType'
replace 'quote' with 'docId'
replace 'user' with 'users'
Can someone say where the error happened. Code gives the error in the line "List<Map<String, dynamic>> userObjects = userInfo['objects'] as List<Map<String, dynamic>>;" in getUserObjects method
Moving the OP's solution into an answer for this question, this specific issue was caused by sending an incorrect document.id in the fetch call.
Using the correct document ID (user.id + datetime for the OP's case) solved the problem.

Flutter/Dart firestore returns LinkedMap - How to use this?

In firestore, I have an array of (simple) maps.
When getting this data into Flutter/Dart, using the cloud_firestore package as follows:
Stream<T> documentStream<T>({
required String path,
required T Function(Map<String, dynamic>? data, String documentID) builder})
{
final DocumentReference<Map<String, dynamic>> reference =
FirebaseFirestore.instance.doc(path);
final Stream<DocumentSnapshot<Map<String, dynamic>>> snapshots =
reference.snapshots();
return snapshots.map((snapshot) => builder(snapshot.data(), snapshot.id));
}
and consuming this e.g. as follows:
documentStream(
path: 'firestore_path_doc',
builder: (data, documentId) {
final x = data['array_of_maps'];
print(x[0].runtimeType); // gives LinkedMap<String, dynamic>
final y = x as List<Map<String, String>>; // fails
final y1 = x[0] as Map<String, String>; // fails
},
);
fails.
I cannot find a way to convert the map inside this array to a normal map.
In fact, I can barely find any info on LinkedMap. After digging, I see its in the js (https://pub.dev/packages/js) package, but it's internal there, not supposed to be exposed.
If I had a map in firestore, it converts easily to a Map<String, String> in dart. But a map inside an array arrives as a LinkedMap.
How can I work this LinkedMap? I cannot even iterate over it, since it is not recognised at compile time, even if I try to import js.
thx
I got it to work following the answer to this: How to convert an array of map in Firestore to a List of map Dart
Basically by using a class for the inner map instead of a generic Map.
x[0] type is Map<String, dynamic>. You're trying to cast it to Map<String, String>, which would obviously fail.
You can do that manually though:
final x = data['array_of_maps'] as List<Map<String, dynamic>>;
final myMap = x[0].map((key, value) => MapEntry(key, value.toString()));

The operator '[]' isn't defined for the type 'Object'. Try defining the operator '[]'. Flutter Firestore [duplicate]

Every thing was working good but when i upgraded my cloud firestore dependecy. I started getting an error "The operator '[]' isn't defined for the type 'Object'. ". This error is coming in front of all the 4 userData.data()[""],
class BaseProvider with ChangeNotifier {
//instances of firebase
final FirebaseAuth _auth = FirebaseAuth.instance;
final CollectionReference postsCollection =
FirebaseFirestore.instance.collection("posts");
final CollectionReference userCollection =
FirebaseFirestore.instance.collection("users");
//Creating post
Future addPost(
) async {
DocumentSnapshot userData =
await userCollection.doc(_auth.currentUser.uid).get();
return await postsCollection.doc().set({
"id": _auth.currentUser.uid,
"sellername": userData.data()["name"], //Error
"sellercontact": userData.data()["phone"], //Error
"sellercity": userData.data()["city"], //Error
"sellerstate": userData.data()["state"], //Error
});
}
Starting at cloud_firestore Version 2.0.0
The class DocumentSnapshot now takes a generic parameter. The declaration:
abstract class DocumentSnapshot<T extends Object?> {
and therefore it contains an abstract method of type T:
T? data();
Therefore you need to do the following:
DocumentSnapshot<Map<String, dynamic>> userData =
await userCollection.doc(_auth.currentUser.uid).get();
return await postsCollection.doc().set({
"id": _auth.currentUser.uid,
"sellername": userData.data()["name"],
"sellercontact": userData.data()["phone"],
"sellercity": userData.data()["city"],
"sellerstate": userData.data()["state"],
});
Now data() method will be of type Map<String,dynamic> and you can access the data as you normally do using the [] operator.
Another Example:
Query query = FirebaseFirestore.instance.collection("collectionPath");
final Stream<QuerySnapshot<Map<String,dynamic>>> snapshots = query.snapshots();
The above will give the error:
A value of type 'Stream<QuerySnapshot<Object?>>' can't be assigned to a variable of type 'Stream<QuerySnapshot<Map<String, dynamic>>>'.
You get this error because Query has the following declaration:
abstract class Query<T extends Object?>
while snapshots() returns the following:
Stream<QuerySnapshot<T>> snapshots({bool includeMetadataChanges = false});
Since a type wasn't specified for Query and since T extends Object?, therefore in the code snapshots() will have the following return type Stream<QuerySnapshot<Object?>> and you will get the above error. So to solve this you have to do:
Query<Map<String,dynamic>> query = FirebaseFirestore.instance.collection("collectionPath");
final Stream<QuerySnapshot<Map<String,dynamic>>> snapshots = query.snapshots();
According to the docs:
BREAKING REFACTOR: DocumentReference, CollectionReference, Query, DocumentSnapshot, CollectionSnapshot, QuerySnapshot, QueryDocumentSnapshot, Transaction.get, Transaction.set and WriteBatch.set now take an extra generic parameter. (#6015).
Therefore you need to implement the above for all those classes.
in my case I simply had to change snapshot.data()['parameter'] to snapshot.get('parameter')
UserModel _userFromFirebaseSnapshot(DocumentSnapshot snapshot) {
return snapshot != null ?
UserModel(snapshot.id,
name: snapshot.get('name'),
profileImageUrl: snapshot.get('profileImageUrl'),
email: snapshot.get('email'),
) : null;
}

How do I cast InternalLinkedHashMap as Map<dynamic, dynamic>?

Im setting up firebase cloud messaging and in my onResume() callback I have the following:
Future<void> onResume(Map<String, dynamic> message) async {
final Map<String,dynamic> data = message['data'];
final String url = data['url'];
if (url != null) {
_webViewController?.loadUrl(url);
}
}
When the function reaches
final Map<String,dynamic> data = message['data'];
it returns prematurely and silently without warnings.
If I instead run
final dynamic data = message['data'];
it continues as expected.
Inspecting the message type revels that message is InternalLinkedHashMap and cannot be cast too Map<String, dynamic>.
It says _InternalLinkedHashMap<dynamic, dynamic>' is not a subtype of type 'Map<String, dynamic>'.
How do I do this?
How can I find this issue in the future if it has no trace?
I found the best way to solve it were to use Map.from() and specify the variable type as Map:
final Map<String, dynamic> data = Map.from(message['data']);
Try this:
final Map<String,dynamic> data = Map.castFrom<dynamic, dynamic, String, dynamic>(message['data']);
You should only do this, of course, if all of your keys are really Strings.
If you want to understand what's going on, see this:
var x = {};
print(x.runtimeType);
Map<String, dynamic> y = Map.castFrom<dynamic, dynamic, String, dynamic>(x);
print(y.runtimeType);
You can try to map all keys to string. Something like:
final Map<String,dynamic> data = message.map((key, value) => MapEntry(key.toString(), value));