I use firebase package for web version of my app and firebase_database for device version.
When querying a specific node using firebase_database the .once() method doesn't ask for any input parameter but firebase counterpart expects a String eventType input. I looked in the API docs but I couldn't find out what should be passed in..
What string should be passed in?
As always thank you very much for the help.
The firebase_database methods:
Future<DateTime> getFirebaseSyncDate(
String cityDb, String regionDb, String countryDb) {
// TODO: implement getFirebaseSyncDate
return ref
.child('Firebase Sync Date')
.orderByChild('Sync Date')
.limitToFirst(1)
.once()
.then((DataSnapshot snap) {
DateTime syncDate = snap.value['Sync Date'];
return syncDate;
});
}
and the firebase version:
Future<DateTime> getFirebaseSyncDate(
String cityDb, String regionDb, String countryDb) {
var ref = firebase.database().ref();
return ref
.child('Firebase Sync Date')
.orderByChild('Sync Date')
.limitToFirst(1)
.once('') // <=== String eventType ??
.then((snap) {
DateTime syncDate = snap.snapshot.val()['Sync Date'];
return syncDate;
});
}
once receive an eventType, one of the following strings: value, child_added, child_changed, child_removed, or child_moved.
In your case, I think it must be value
Check this link for more details
Related
I'm trying to set a variable token to the data I retrieve from Firebase Realtime database. token is assigned within a if statement and the assignment works but outside of the if statement, token becomes null. token is declared outside of the if statement so I'm a bit confused why it's not being assigned to the data from Firebase.
sendNotification(
BuildContext context, String title, String message, String userID) async {
DatabaseReference databaseReference = tokensRef.child(userID);
DataSnapshot snapshot = await databaseReference.once();
String token;
if (snapshot != null) {
databaseReference.get().then((result) {
Map<dynamic, dynamic> values = result.value;
if (values == null) {
token = null;
} else {
token = values['deviceToken'];
//print(token);
}
});
}
print(token);
}
The get() call loads data from the database, which happens asynchronously (since it may take some time). While the data is being loaded, the rest of your code continues execute so that the user can continue using the app. Then when the data is available, your then is executed.
This means that your print(token) outside of the then now runs before the token = values['deviceToken'] is ever execute, so it will never print the value you want because that value hasn't loaded yet.
For this reason, any code that needs the data from the database needs to be inside the then code block, like your commented //print(token).
If you want to return the data, you will need to return the Future that get() returns.
Alternatively you can use async/await, which make the then part a bit more recognizable:
if (snapshot != null) {
var result = await databaseReference.get()
Map<dynamic, dynamic> values = result.value;
if (values == null) {
token = null;
} else {
token = values['deviceToken'];
}
}
print(token);
Behind the scenes this still does the exact same thing. The compiler just generates some of that code for you, allowing you to look at something that's a bit closer to what you're probably used to.
Also see:
The Flutter documentation on asynchronous programming: futures, async, await.
how to check whether the dataSnapshot null on Flutter Firebase (Realtime) Database plugin
How to use await instead of .then() in Dart
Prior to the update, document['field'] would result in null if it did not exist. Now it throws a Bad State error. The null response is needed because the field does not exist in some historical data and/or optional data in the Firebase collection. Confirmed this in the following report as well:
https://github.com/FirebaseExtended/flutterfire/issues/3826
Is there a way to capture the error and ignore it or set it to null or empty string?
static Pool dataFromDocument(QueryDocumentSnapshot document) {
return Pool()
..authUID = document.get('authUID')
..documentID = document.id
..propertyManagerID = document['propertyManagerID'] as String
}
static Stream<QuerySnapshot> getData(String authUID) {
CollectionReference poolRef = FirebaseFirestore.instance.collection(dataDB);
Query query = poolRef.where('authUID', isEqualTo: authUID.trim());
return query.snapshots();
}
With null safety, String is not nullable. However, String? is.
Change the line:
..propertyManagerID = document['propertyManagerID'] as String
To:
..propertyManagerID = document['propertyManagerID'] as String?
Your Pool will also have to have the property propertyManagerID as a String? in the class definition.
I'm trying to get the key of a child that I've already returned from the database. In the example below, I have the user selected as Carlos Sainz and I'm trying to get "csainz" however the code below returns "email".
DatabaseReference keyRef = FirebaseDatabase.instance.reference();
await keyRef.child('users')
.child('email')
.equalTo(userList[0].email) // userList[0].email returns Carlos' email
.once()
.then((DataSnapshot dataSnapshot) {
String newKey = dataSnapshot.key;
print(newKey);
});
Here is how my db is setup:
Two problems:
To order/filter on a specific property of each child node, you need to use orderByChild(...) and not just child(...). Right now your code reads /users/email, which doesn't exist.
When you execute a query against the Firebase Database, there will potentially be multiple results. So the snapshot contains a list of those results. Even if there is only a single result, the snapshot will contain a list of one result.
Your code doesn't handle the list, but prints the key of the location against which the query was executed: users.
So to fix both would look something like this:
DatabaseReference keyRef = FirebaseDatabase.instance.reference();
await keyRef.child('users')
.orderByChild('email')
.equalTo(userList[0].email)
.onChildAdded.listen((Event event) {
print('${event.snapshot.key}');
}, onError: (Object o) {
final DatabaseError error = o;
print('Error: ${error.code} ${error.message}');
});
});
You can also use .once().then(...) but will then have convert dataSnapshot.value to a map, and show the key(s) from that map. Not check, but it should be something like this:
DatabaseReference keyRef = FirebaseDatabase.instance.reference();
await keyRef.child('users')
.orderByChild('email')
.equalTo(userList[0].email)
.once()
.then((DataSnapshot dataSnapshot) {
String newKey = dataSnapshot.value.keys[0];
print(newKey);
});
Im trying to get the User Id of the current user signed in. This is how I;m trying to get the UserID as String, where the functions are printing the $userId and also $uid.
String userId;
Future<String> getUser() async {
final FirebaseUser user = await FirebaseAuth.instance.currentUser();
final String uid = user.uid.toString();
print("uid in getuser function $uid");
userId = uid;
return uid;
}
And this is the Stream in which im trying to get the UserID(uid) into (both the Futures and Stream are in the same class)(the return statement in the Stream can be ignored)
// get hotel stream
Stream<List<Hotel>> get userHotels {
print("This is the uid ${getUser().toString()} in Stream");
return ownerCollection.document(uid).collection("hotels").snapshots()
.map(_hotelListFromSnapshot);
}
Printing $getUser().toString() gives this in the console : "This is the uid Instance of 'Future' in Stream" and not printing the actual UserID.
And printing $userId gives this in the console : " This is the uid null in Stream"
Please help, I want to get the UserId of the current user signed in the Stream
As defined above getUser() returns a Future<String>. So toString() on that will return Instance of 'Future'.
What you probably want to do is wait for getUser() to finish first.
You can do that by using async generators, with async* and yield*.
// get hotel stream
Stream<List<Hotel>> get userHotels async* {
final uid = await getUser();
print("This is the uid $uid in Stream");
yield* ownerCollection.document(uid).collection("hotels").snapshots()
.map(_hotelListFromSnapshot);
}
Using async* means you'll return a Stream. Then after waiting for getUser() to finish, you can put values on that stream with yield. yield* specifically means you are putting the values of another Stream on the one you are returning.
Your function getUser() will work the the same as the FirebaseAuth.instance.currentUser() function you are using, and will return a future. If you want to print it, you need to either use await as you did before, or the then function.
When it comes to using it in your function, you need to use the result of the future to create the stream. Take a look at this answer for how to do that.
I have a class Product and it is in List plist
Now I need to call the firebase database.set(plist) this is working with Java but when I tried to do it with flutter dart it showing error anybody have the solution for this problem
From StackOverflow, I understand use database.set('{"a":"apple"}) but when I am dealing with List I can't use this solution
update error message
error called Invalid argument: Instance of 'Product'
My code
String table_name="order";
FirebaseAuth.instance.currentUser().then((u){
if(u!=null){
FirebaseDatabase database = FirebaseDatabase(app: app);
String push=database.reference().child(table_name).child(u.uid).push().key;
database.reference().child(table_name).child(u.uid).child(push).set( (productList)).then((r){
print("order set called");
}).catchError((onError){
print("order error called "+onError.toString());
});
}
});
}
We cannot directly set object in Firebase. Unfortunately in Flutter there is no easy solution like java json.
Data types that are allowed are String, boolean, int, double, Map, List. inside database.set().
We can have a look at the official documentation of Flutter https://pub.dev/documentation/firebase_database/latest/firebase_database/DatabaseReference/set.html
Try setting object like this
Future<bool> saveUserData(UserModel userModel) async {
await _database
.reference()
.child("Users")
.child(userModel.username)
.set(<String, Object>{
"mobileNumber": userModel.mobileNumber,
"userName": userModel.userName,
"fullName": userModel.fullName,
}).then((onValue) {
return true;
}).catchError((onError) {
return false;
});
}
I hope this code will be helpful.
Extending a little bit an answer given as a comment above
You basically have to create an auxiliary map beforehand:
Map aux = new Map<String,dynamic>();
And then iterate through the array that you have adding the corresponding map for each child that you want to add:
productList.forEach((product){
//Here you can set the key of the map to whatever you like
aux[product.id] = product.toMap();
});
Just in case, the function toMap inside the Product class should be something like:
Map toMap() {
Map toReturn = new Map();
toReturn['id'] = id;
toReturn['name'] = name;
toReturn['description'] = description;
return toReturn;
}
And then, when you are calling the set function to save to firebase you can do something like:
.set({'productList':aux,})
Hope this was helpful to someone.