I am learning how to use "Firestore" with BLoC pattern in Flutter. I am following this tutorial by Sagar Suri. https://medium.com/codechai/when-firebase-meets-bloc-pattern-fb5c405597e0. However, this tutorial is old and I am trying to remove bugs and update it for learning purpose. I am facing 2 issue in it. First issue is related with 'updateGoal' function. In example, he copied goals value from collection , cast it into the String and then updated the value. I am getting an error here. Anybody can help me, how I can extract goals value from users, copy into Map, cast it and then update. . This is what I am trying to do.
Future<void> uploadGoal(String title, String documentId, String goal) async {
DocumentSnapshot doc =
await _firestore.collection("users").doc(documentId).get();
Map<String, String> data = doc.data()! as Map<String, String>;
/****/
//Getting error here "The operator '[]' isn't defined for the type 'Object? Function()'."
Map<String, String> goals = doc.data["goals"] != null
? doc.data["goals"].cast<String, String>()
: null;
/****/
if (data != null) {
data[title] = goal;
} else {
data = Map();
data[title] = goal;
}
return _firestore
.collection("users")
.doc(documentId)
.set({'goals': data, 'goalAdded': true}, SetOptions(merge: true));
}
Similar issue, I am facing in removeGoal function.
void removeGoal(String title, String documentId) async {
DocumentSnapshot doc =
await _firestore.collection("users").doc(documentId).get();
Map<String, String> data = doc.data()! as Map<String, String>;
//How to remove goals title from collection here
goals.remove(title);
if (goals.isNotEmpty) {
_firestore
.collection("users")
.doc(documentId)
.update({"goals": goals});
} else {
_firestore
.collection("users")
.doc(documentId)
.update({'goals': FieldValue.delete(), 'goalAdded': false});
}
}
Anybody can help me? Thanks.
This looks wrong:
Map<String, String> data = doc.data()! as Map<String, String>;
While all the keys in your document are strings, the goals value is an object/dictionary instead of a string. So at best you can cast it to:
Map<String, dynamic> data = doc.data()! as Map<String, dynamic>;
Once you do that, the statement you commented out to get the goals field should work, but it it doesn't: provide an updated in to your question with the updated code, and the exact error message and stack trace you get.
Related
This question already has answers here:
"The operator '[]' isn't defined" error when using .data[] in flutter firestore
(6 answers)
Closed 3 months ago.
I would like to recover data in my firebase database but it does not work.
void _userData() async {
DocumentReference documentReference = FirebaseFirestore.instance
.collection("Users")
.doc("axelduf2006#gmail.com");
documentReference.get().then((datasnapshot) {
data = datasnapshot.data;
return print("pseudo: ${data['pseudo']}");
});
}
my log console
I would like to know the value contained in pseudo in my database.
Change data with data() to get the Map<String, dynamic> of your document.
void _userData() async {
DocumentReference documentReference = FirebaseFirestore.instance
.collection("Users")
.doc("axelduf2006#gmail.com");
documentReference.get().then((datasnapshot) {
data = datasnapshot.data() as Map<String, dynamic>; // set it like this
return print("pseudo: ${data['pseudo']}");
});
}
Passing the data will pass the definition of Map<String, dynamic> Function ( () => Map<String, dynamic> ), not the actual Map<String, dynamic> of the document data.
I am trying to fetch data from a local json file in my assets. I received and decoded data and then tried to store it in a list for further use. But it give me the exception. I want the list to be of type OriginDestination, store it in database and then use it further. Can someone please tell me how can I parse data from json to OriginDestination.
Class OriginDestination -
OriginDestination cityDtoFromJson(String str) => OriginDestination.fromJson(json.decode(str));
String cityDtoToJson(OriginDestination data) => json.encode(data.toJson());
#HiveType(typeId: 0)
// ignore: non_constant_identifier_names
class OriginDestination extends HiveObject {
#HiveField(0)
City? origin;
#HiveField(1)
List<City>? destinations;
OriginDestination({
this.origin,
this.destinations,
});
factory OriginDestination.fromJson(Map<String, dynamic> json) => OriginDestination(
origin: City.fromJson(json["origin"]),
destinations: List<City>.from(
json["destinations"].map((x) => City.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"origin": origin,
"destinations": destinations,
};
Code where I am fetching data and want to use it (originDestinations is also a list of type OriginDestination) -
List<OriginDestination>? localOriginDestination = [];
Future<void> readJson() async {
final String response = await rootBundle.loadString('assets/files/node.json');
final data = await json.decode(response);
localOriginDestination = await data["data"].cast<OriginDestination>();
print(localOriginDestination);
if(localOriginDestination!=null) {
await _localStorageService.getBox().clear();
await _localStorageService.getBox().addAll(localOriginDestination!.toList());
originDestinations = _localStorageService.getOriginDestinations();
}
}
I have never used Hive before but this line of code seems suspicious to me:
localOriginDestination = await data["data"].cast<OriginDestination>();
I think you want to do something along the lines of:
localOriginDestination = [
for (final element in await data['data']) OriginDestination.fromJson(element),
];
But I can't be entirely certain without knowing what the value of await data['data'] is.
Edit, adding some more information.
The .cast method on List is only meant to be used with a type that is a subtype of the original list type, like in this example:
void main() {
List<num> nums = [1, 2, 3];
List<int> ints = nums.cast<int>();
ints.add(4);
print(ints);
}
What you are doing is essentially the same thing as this.
void main() {
List<Map<String, int>> items = [
{'A': 1, 'B': 2},
{'A': 3, 'B': 4},
];
final result = items.cast<ABC>();
print(result);
}
class ABC {
int a;
int b;
ABC.fromJson(Map<String, dynamic> json)
: a = json['A'],
b = json['B'];
}
The problem here is that the ABC class is not a subtype of Map<String, int>. Classes are not maps in the dart programming language. You need to need to call the .fromJson constructor on each element of the items list in order to get a List<ABC>.
You can do it in a few different ways.
Using map method is one approach. (you need to be on dart 2.15+ for this exact syntax)
List<ABC> result = items.map(ABC.fromJson).toList();
Looping over items with a collection for is another approach.
List<ABC> result = [for (final element in items) ABC.fromJson(element)];
Looping over items with a conventional for loop is yet another approach.
List<ABC> result = [];
for (final element in items) {
result.add(ABC.fromJson(element));
}
I'm trying to get a specific field called "specie" from a document in a Firebase collection. I am trying as follows but I have an error of type 'Future ' is not a subtype of type 'String'. What am I doing wrong?
Repository method:
getSpecie(String petId) {
Future<DocumentSnapshot> snapshot = petCollection.document(petId).get();
return snapshot.then((value) => Pet.fromSnapshot(value).specie);
}
Entity method:
factory Pet.fromSnapshot(DocumentSnapshot snapshot) {
Pet newPet = Pet.fromJson(snapshot.data);
newPet.reference = snapshot.reference;
return newPet;
}
factory Pet.fromJson(Map<String, dynamic> json) => _PetFromJson(json);
Pet _PetFromJson(Map<String, dynamic> json) {
return Pet(json['name'] as String,
specie: json['specie'] as String);
}
I found a solution. No needed fromJson() method, I only changed the repository method:
Future<String> getSpecie(String petId) async {
DocumentReference documentReference = petCollection.document(petId);
String specie;
await documentReference.get().then((snapshot) {
specie = snapshot.data['specie'].toString();
});
return specie;
}
Try this..
getSpecie(String petId) async{
Future<DocumentSnapshot> snapshot = await petCollection.document(petId).get();
return snapshot.then((value) => Pet.fromSnapshot(value).specie);
}
This is how I learned to get documents from firestore
https://medium.com/#yasassandeepa007/how-to-get-sub-collection-data-from-firebase-with-flutter-fe1bda8456ca
I trying to get the new created document id after data has been stored to firebase database, but get error
E/flutter (20333): [ERROR:flutter/lib/ui/ui_dart_state.cc(148)] Unhandled Exception: NoSuchMethodError: The method 'listen' was called on null.
E/flutter (20333): Receiver: null
E/flutter (20333): Tried calling: listen(Closure: (String) => void)
send_data_bloc
_repository
.addOrder(order)
.listen((documentId) => print(documentId));
repository
#override
Observable<String> addOrder(Order order) {
var a = endpoints.collectionEndpoint.add(order.toJson());
a.then((val) {
return Observable.fromFuture(val.documentID());
});
endpoints
#override
get collectionEndpoint => _firestore
.collection(collectionName)
.document(this.id)
.collection(orderCollectionName);
Ideally you should return the future from the repository and await for the future on the bloc. Let me try to give a full code snippet here. It would be something like this:
send_data_bloc
final documentId = await _repository
.addOrder(order);
print(documentId);
return documentId;
repository
#override
Future<String> addOrder(Order order) {
return endpoints.collectionEndpoint.add(order.toJson());
endpoints
#override
get collectionEndpoint => _firestore
.collection(collectionName)
.document(this.id)
.collection(orderCollectionName);
Here
a.then((val) {
return Observable.fromFuture(val.documentID());
});
you are returning the observable within the then function, i believe this is not the expected behavior.
One thing you should do to improve your code quality and readability is to just user async/await. The function on the repository can be rewrited like that:
#override
Observable<String> addOrder(Order order) async {
var documentID = await endpoints.collectionEndpoint.add(order.toJson());
return Observable.fromFuture(val.documentID());
Try this. This should do the trick.
Whats the reason why you are using Observables? Is this a firebase thing?
You could adjust to:
final var documentId = await _repository.addOrder(order);
print(documentId)
i had the same problem here is a snippet of how i approached it
//notice im using add while referencing the document reference
final DocumentReference documentReference=await Firestore.instance.collection('jobs').add({
'jobid':"",
});
then get your id from documentReference
final String jobIdd=documentReference.documentID;
after getting the id now you can add your document to cloud firestore
Firestore.instance.collection('jobs').document(jobIdd).setData({
'category': category,
'description': description,
'datePosted': formattedDate,
'postedby': userid,
'jobid':jobIdd,
});
I'm unable to find solutions from the previously available question, I have cast json string to map
Below is my API calling method.
Future<EventResponse> fetchEvent( ) async { // here i change Future type
String url='http://xxxxxxxxxxxx.tk/api/userapp/event/lists';
var headers = new Map<String, String>();//here i defined Map type
headers['Auth-Key'] = 'OCDOC#2018';
headers['End-Client'] = 'OCDOC';
var body = new Map<String, String>();//here i defined Map type
headers['schedule'] = 'present';
http.Response res = await http.post(url,headers: headers, body: body);
final parsed=json.decode(res.body);
var myMap = Map<String, dynamic>.from(parsed);
EventResponse eventResponse = EventResponse.convertEventResponse(myMap);
return eventResponse;
}
this is my convertEventResponse methode
factory EventResponse.convertEventResponse(Map<String, dynamic> json) {
List<dynamic> events = json['eventList'];
List<Event> eventList = events.map((e) => Event.convertEvent(e)).toList(); //here i changed by #Richard Heap answer
return EventResponse(
error: json['error'],
status: json['status'],
deliveryCharges: json['deliveryCharge'],
imageBaseUrl: json['image_base_url'],
imageLogoUrl: json['image_logo_url'],
eventList: eventList,
);
}
The error i'm getting.
InternalLinkedHashMap<String, dynamic>' has no instance method 'cast' with matching arguments.
Use instead
.cast<String,dynamic>();
See also https://api.dartlang.org/stable/2.0.0/dart-core/Map/cast.html
Usually it's better to use Map<String,String>.from(oldMap) instead of cast<...>(...)