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,
});
Related
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.
I'm trying to implement a getter, but it is showing me this error on last line in the below code snippet.
The code is -
class AuthRepository extends BaseAuthRepository {
final FirebaseFirestore _firebaseFirestore;
final auth.FirebaseAuth _firebaseAuth;
AuthRepository({
FirebaseFirestore? firebaseFirestore,
auth.FirebaseAuth? firebaseAuth,
}) : _firebaseFirestore = firebaseFirestore ?? FirebaseFirestore.instance,
_firebaseAuth = firebaseAuth ?? auth.FirebaseAuth.instance;
#override
// TODO: implement user
Stream<auth.User> get user => _firebaseAuth.userChanges();
try this
Stream<auth.User?> get user => auth.userChanges();
change
Stream<auth.User> get user => _firebaseAuth.userChanges();
to
Stream<auth.User> get user => _firebaseAuth.userChanges()!;
As can be seen in the documentation,
The method returns an object of Stream<User?>, which means that it may be null.
Since you can't be sure of the contents of the type returned, instead of returning it, you can check if it is null.
I.E.
class AuthRepository extends BaseAuthRepository {
final FirebaseFirestore _firebaseFirestore;
final auth.FirebaseAuth _firebaseAuth;
AuthRepository({
FirebaseFirestore? firebaseFirestore,
auth.FirebaseAuth? firebaseAuth,
}) : _firebaseFirestore = firebaseFirestore ?? FirebaseFirestore.instance,
_firebaseAuth = firebaseAuth ?? auth.FirebaseAuth.instance;
#override
// TODO: implement user
Stream<auth.User> get user =>
val userChanges = _firebaseAuth.userChanges();
if (userChanges != null) return userChanges
else //your logic
_firebaseAuth.userChanges() returns User? which is nullable so you need to update your return type to be nullable by adding ? after auth.User like below:
Stream<auth.User?> get user => _firebaseAuth.userChanges();
This it worked for me:
Stream<auth.User?> get user => _firebaseAuth.userChanges();
It sends out events when a user's credentials are linked or unlinked, as well as when the user's profile is updated. This Stream's goal is to listen to real-time changes to the user state (signed-in, signed-out, different user & token refresh) without having to manually perform reload and then return changes to your application.
Check the documentation for more information.
I recently started developing an app using Flutter and Firebase. I use Firebase Emulator to test Authentication and Cloud Functions. Most of my code is in the Firebase Cloud Functions which I use for all CRUD for Firestore and RTDB. While adding some new features, I got this error in my app. I tried searching a lot but could not find any solution. The following is the error is receive:
An error occured while calling function profile-get
Error Details: null
Message: An internal error has occurred, print and inspect the error details for more information.
Plugin: firebase_functions
Stacktrace: null
My API class in Flutter:
class Api {
Api(this.functions);
final FirebaseFunctions functions;
static Api init() {
FirebaseFunctions functions = FirebaseFunctions.instance;
if (emulator) functions.useFunctionsEmulator(origin: host);
return Api(functions);
}
Future<ApiResult> call(String name, {
Map<String, dynamic> parameters,
}) async {
try {
HttpsCallable callable = functions.httpsCallable(name);
HttpsCallableResult results = await callable.call(parameters);
return ApiResult(new Map<String, dynamic>.from(results.data));
} on FirebaseFunctionsException catch (e) {
print('An error occurred while calling function $name.');
print('Error Details: ${e.details}\nMessage: ${e.message}\nPlugin: ${e.plugin}\nStacktrace: ${e.stackTrace}');
return ApiResult({
'status': 'error',
'message': 'An error occured',
'code': 'unknown'
});
}
}
static String get host => Platform.isAndroid ? 'http://10.0.2.2:2021' : 'http://localhost:2021';
}
I tried running the functions directly from their local URL and they work fine.
As mentioned in the comments defore you are reating a cloud function with onRequest. Those are not callable using an SDK but only trough https URL.
To create a callable function that you can call trough Firebase SDKs you would need to refactor your functions to use the onCall.
It should look something like this:
exports.yourFunctionName= functions.https.onCall((data, context) => {
// receive the data
const text = data.text;
// return a response
return {
test:'test'
}
});
Here you have more information how the callable functions work.
Are you using a different region than the standard us-central1? This is often the case, so you need to change the region you are calling from
HttpsCallable callable = FirebaseFunctions.instanceFor(region:"your_region").httpsCallable(name);
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 would like to write data in Firebase and I get a permission error, here is what I tried :
void initState() {
super.initState();
testFirebase();
}
Future testFirebase() async {
initUser();
//Initialize Firebase
final FirebaseApp firebaseApp = await FirebaseApp.configure( ... );
final FirebaseDatabase database = new FirebaseDatabase(app: firebaseApp);
database.reference().child('counter').push().set(<String, String>{
'var': 'test'
});
}
Future initUser() async {
googleUser = await _ensureLoggedInOnStartUp();
if (googleUser == null) {
setState(() {
state.isLoading = false;
});
} else {
var firebaseUser = await logIntoFirebase();
}
}
Here is my Firebase rules :
The google-services.json file is added to the app root directory :
Result :
I get the following error message :
I tried also with :
push().setValue('2')
but it doesn't work and that makes me crazy, I don't understand...
Any idea?
Quick initial check is that you need to await initUser(). So:
Future testFirebase() async {
await initUser();
//Initialize Firebase
final FirebaseApp firebaseApp = await FirebaseApp.configure( ... );
Without that I'd expect the calls to the database to start before auth has finished.
Update: I just verified that is indeed the case with this simple code:
void _signin() async {
print("Calling __actuallySignin");
__actuallySignin();
print("called __actuallySignin and waited");
}
void __actuallySignin() async {
print("Calling signIn...");
await FirebaseAuth.instance.signInAnonymously();
print("called signIn... and waited");
}
This prints:
flutter: Calling __actuallySignin
flutter: called __actuallySignin and waited
flutter: Calling signIn...
...
flutter: called signIn... and waited
So the __actuallySignin method is done before the sign in is done. To make the calling code wait for the result you add await:
await __actuallySignin();
Which outputs:
flutter: Calling __actuallySignin
flutter: Calling signIn...
flutter: called signIn... and waited
flutter: called __actuallySignin and waited
Just adding await to initUser() didn't work, the database was also not correctly adressed.
Solution :
FirebaseDatabase.instance.reference().child ...
instead of :
final FirebaseApp firebaseApp = await FirebaseApp.configure( ... );
final FirebaseDatabase database = new FirebaseDatabase(app: firebaseApp);
I was able to fix this problem by not accessing the database reference using the method listed in the plugin documentation:
final FirebaseDatabase database = FirebaseDatabase(app: app);
this.databaseRef = database.reference();
But instead making use of this approach:
this.databaseRef = FirebaseDatabase.instance.reference();
When you work with multiple pieces of Flutter documentation, you will see that many approaches work for reading public information from the Real Time Database, but only one method worked for reading information that depends on the auth variable. This was my experience.
Working method: https://marcinszalek.pl/flutter/firebase-database-flutter-weighttracker/
Non-working method was associated with the plugin documentation: https://pub.dartlang.org/packages/firebase_database#-example-tab-
Also, my project was configured using the main Firebase Flutter documentation for linking my app to Firebase: https://firebase.google.com/docs/flutter/setup