How to check whether a user exists in firebase cloudstore or not in a flutter application while reigisteration is done? - flutter

When i try to check whether a particular number exists in firebase collection or not I always get a null in return. What is the reason for that?
How can I overcome this issue so that the function _numberCheck() returns a Boolean value which I can use for further processing?
The code for the registration page is as follows:
Future<bool> _numberCheck(mobileno) async {
print("Start of the function");
db
.collection("transcriber_user_registeration")
.getDocuments()
.then((QuerySnapshot snapshot) {
snapshot.documents.forEach((f) async {
isPresent = true;
isPresent = false;
if ('${f.data['userid']}' == mobileno) {
setState(() {
isPresent = true;
});
} else {
setState(() {
isPresent = false;
});
}
});
});
// print(isPresent);
return isPresent;
}

For your case, you can try to use a query that only returns values that are not null. This way, in case it's not null, it will return true and if it's null, it will return false.
One example of how to achieve that - as better explained in this other question from the Community here - you can use the function hasAll(). This function returns only if all values are present. So, you would use it to load all the values to array, verifying this way, if they are not null - something like the below example.
!resource.data.keys().hasAll(['mobileno'])
There are other examples, such as using a String to compare values using the where() function, so you won't get the null values. More information on doing queries are available in this link.
I would recommend you to take a look at them and give these above options a try, to verify if they will help you. :)
Let me know if the information helped you!

You function returns null because you are returning isPresent without setting it to true, you only set isPresent = false, which means isPresent will be null otherwise, as variables hold objects references, so it will hold null if you didn't initialize it.
bool isPreset; // isPreset is null
isPreset = true; // isPreset is true
isPreset = false; // isPreset is false
If a variable lacks a value, it's value is null. Always initialize your variables to something.
Also, if f.data['userid'] is a String, you don't have to make it a String again by surrounding it with '${}'
Change your code to:
Future<bool> _numberCheck(mobileno) async {
QuerySnapshot snapshot = await db.collection("transcriber_user_registeration")
.getDocuments();
snapshot.documents.forEach((f) {
if (f.data['userid'] == mobileno) {
return false;
}
});
return true;
}
And call setState for the result of this function where you call it. Or just use a FutureBuilder to deal directly with the result and refresh your screen automatically.
If it returns true it means the query collection result is an empty collection or the if (f.data['userid'] == mobileno) is never true. So check the values on both.

Related

My firebase callable cloud function is called twice (when I called only once), how can I avoid my flutter app from failure?

I am trying to make a callable function in firebase, but when I call it from my flutter app it is called twice.
The Issue is, I am calling the function while sending some parameters to perform on by cloud function.
The automatic second call initiates with null parameter, which causes chaos because the function return empty list.
Here is my function code.
callable function
exports.getItem = functions.https.onCall(async (data, context) => {
// Data NULL Check
if (data == null) {
console.log("NULL DATA RECIEVED");
console.log(`null event id ${context.eventID}`);
} else {
console.log(`event id ${context.eventID}`);
const itemList = [];
console.log("Begin...............");
console.log(data);
console.log(data["closet"]);
console.log(data["from"].toString());
console.log(data["count"].toString());
const querySnapshot = await admin.firestore().collection("t_shirt").get();
querySnapshot.doc.forEach((doc)=>{
itemList.push(doc);
});
console.log(itemList.length);
if (itemList.length > 0) {
return itemList;
}
}
});
As I know I have to return something, because it's a promise.
So If I return empty list if the data is null, the app recieves empty list, while the first call is still fetching the data and making a list.
Okay the issue was it's querySnapshot.docs and not querySnapshot.doc

Firestore transaction futures are confusing me

I've been trying to figure out transactions all morning and I'm stuck. I keep going around in circles, and I know what I need to do(I think), but I'm not sure how to do it.
Here's my code:
Future<String> obtainUniqueUsername(
String requestedUsername,
String currentUserID,
) async {
var userID = currentUserID;
var userName = requestedUsername.toLowerCase();
try {
// Create a reference to a usernames collection .doc(id)
final userRef = FirebaseFirestore.instance.doc('usernames/$userName');
// Start a transaction to check if the username is assigned
// if assigned a .doc(userName) will exist.
FirebaseFirestore.instance.runTransaction((transaction) async {
var userSnapshot = await transaction.get(userRef);
if (userSnapshot.exists) {
// Username is assigned as .doc(userName) exists
return 'Username is already assigned';
} else {
// Assign username by created .doc(userName)
// insert document reference to user
transaction.set(userRef, {'uid': userID});
}
}).then((value) => 'Username assigned', onError: (e) => 'Transaction error ${e.toString()}');
} // endtry
catch (e) {
return e.toString();
} // endcatch
return 'Transaction has failed, please try again later';
} // end
My problem is that it keeps hitting the final return statement, even if it has created the document. My understanding is that the transaction will keep trying until it is successful and returns a value, or it times out and throws an error. I've read that using .then doesn't await a value, and the function continues uninterrupted until it hits the end, but shouldn't it be either a value or an error?
I feel like I'm missing the point somewhere, sorry if this is super basic, I've really been trying to get it to work.
I got it sorted out! It took forever though, lots of trial and error. I think my issue was that I didn't understand how to return the value from the future using the dart shorthand from the examples I was following.
My understanding now is that I'm awaiting usernameAssigned to complete, and the future is completed when I return 'success'.
If the transaction didn't go through, it would throw an exception, and be caught and I would get a message about what went wrong. I think the assignment of transactionStatus isn't required, but I kind of like having it there for my own understanding of what's happening. Maybe when I get more experience I wont need stuff like that.
I also didn't really know how to use a try/catch block, but I think I've improved how I do that as well. Now, I throw an exception if something weird happens instead of going right to return. I think that's better?
Also, the comment about letting firestore assign the unique uid: Yeah, you are right, but I want to have firestore enforce unique human readable usernames. The autogenerated uid is used for everything else.
The usecase is having a usernames collection built like: .doc(uniqueUsername).data({"uid:" uid})
Firestore won't let duplicate doc ID's happen in that collection, so it gives me what I want. My understanding is this is one of the "easier" ways to enforce unique usernames.
Anyways, thanks for the comments and please send me any feedback you may have!
Future<String> obtainUniqueUsername(
String requestedUsername,
String currentUserID,
) async {
Future<String> obtainUniqueMarkerUsername(
String requestedUsername,
String currentUserID,
) async {
var userID = currentUserID;
var userName = requestedUsername.toLowerCase();
var transactionStatus = '';
try {
// Start a transaction to check if the username is assigned
// if assigned a .doc(userName) will exist.
var usernameAssigned = await FirebaseFirestore.instance
.runTransaction((transaction) async {
// Create a reference to a usernames collection .doc(id)
var userRef = FirebaseFirestore.instance.doc('usernames/$userName');
var userSnapshot = await transaction.get(userRef);
if (userSnapshot.exists == true) {
// Username is assigned as .doc(userName) exists
throw Exception('Username already exists');
} else {
// Assign username by created .doc(userName)
// insert document reference to user
transaction.set(userRef, {"uid": userID});
return 'Success';
}
});
transactionStatus = usernameAssigned;
} // endtry
catch (e) {
return e.toString();
} // endcatch
userID = '';
userName = '';
return transactionStatus;
} // end

How to display categories based on user role, Firebase Flutter

I have 2 kinds of users "d" and "p".
For them, there is a special collection - roles, which stores the user's uid and his role.
I'm trying to display a role and, based on the role, display the corresponding categories.
However, I am getting the following error.
The argument type 'Future<QuerySnapshot<Object?>>? Function(QuerySnapshot<Map<String, dynamic>>)' can't be assigned to the parameter type 'FutureOr<QuerySnapshot<Object?>> Function(QuerySnapshot<Map<String, dynamic>>)' because 'Future<QuerySnapshot<Object?>>?' is nullable and 'Future<QuerySnapshot<Object?>>' isn't.
What alternatives do I have to implement the same? The action takes place in a function that is then called and processed in the FutureBuilder.
getCategoryData(String _collection, {bool forMap = false}) {
FirebaseFirestore.instance.collection('roles').where('uid', isEqualTo: FirebaseAuth.instance.currentUser!.uid).get().then((value){
for (var element in value.docs) {
if(forMap == false && element.data()['role'] == 'd'){
Future<QuerySnapshot> _snapshot = FirebaseFirestore.instance.collection(_collection).where('for_d', isEqualTo: true).get();
return _snapshot;
}else if((forMap == false && element.data()['role'] == 'p') || (forMap == true)){
Future<QuerySnapshot> _snapshot = FirebaseFirestore.instance.collection(_collection).get();
return _snapshot;
}
}
});
}
The problem here is that, there is a chance your getCategoryData function might return a null object.
If it goes over all the docs and none of them match the given two if conditions, a null value is returned by default.
Your database design might be such that this might never occur but the compiler is unaware of that.
You can fix this by returning an empty future after the for loop. The compiler won’t show this error anymore. Although I hope you’ve designed your database well that this condition will never occur.

Problems reading Bluetooth Device data and returning it into code in Flutter/Dart

The following is the function code I am using to read the data from the Bluetooth Device
Future listServices() async {
List<BluetoothService>? services = await primBms?.device.discoverServices();
List values = [];
services?.forEach((service) async {
if (service.uuid.toString() == primBmsUuids[0]) {
for (BluetoothCharacteristic c in service.characteristics) {
List<int> value = await c.read();
int val = int.parse(String.fromCharCode(value[0]) + String.fromCharCode(value[1]));
print(value);
print(val);
values.add(val);
}
}
});
return values;
}
I am parsing the character codes from the read data packet, however using this function returns an empty list. I used a print function to confirm whether value and val return valid values and they do, however for whatever reason these do not get added to the list.
Can someone kindly help me figure out the solution to my problem ?

Cannot get subcollection along with collection in flutter

I am trying to get documents with their own subcollections, from Stream, but I am stuck.
This is where I set up my StreamSubscription:
Future<void> _toggleOrdersHistorySubscription({FirebaseUser user}) async {
_ordersHistorySubscription?.cancel();
if (user != null) {
_ordersHistorySubscription = ordersRepository
.ordersHistoryStream(userId: user.uid)
.listen((ordersSnapshot) {
final List<OrderModel> tmpList = ordersSnapshot.documents.map((e) {
// e.reference.collection("cart").getDocuments().;
return OrderModel.orderFromSnapshot(e);
}).toList();
add(OrdersHistoryUpdated(ordersHistory: tmpList));
});
}
}
The issue is that I can't see a way to get subcollection along with the parent document because getDocuments returns a Future.
Anyone can clear this issue for me?
So, I update the code method a separate method for retrieving data when listener is triggered but it doesn't work fully and I do not understand what's happening and why part of the code is working and part is not.
List<OrderModel> _getOrdersHistory({
#required QuerySnapshot snapshot,
}) {
return snapshot.documents.map((document) {
List<OrderedProductModel> cart = [];
List<AddressModel> addresses = [];
document.reference.collection("cart").getDocuments().then((snapshot) {
snapshot?.documents?.forEach((doc) {
cart.add(OrderedProductModel.fromSnapshot(doc));
});
});
document.reference
.collection("addresses")
.getDocuments()
.then((snapshot) {
snapshot?.documents?.forEach((doc) {
addresses.add(AddressModel.addressFromJson(doc.data));
});
});
final order = OrderModel.orderFromSnapshot(
document,
restaurantCart: cart,
);
return order.copyWith(
orderAddress:
(addresses?.isNotEmpty ?? false) ? addresses.first : null,
sentFromAddress:
(addresses?.isNotEmpty ?? false) ? addresses.last : null,
);
})
.toList() ??
[];
}
As an alternate solution to my original issue is that I made a map entry in Firestore instead of a collection for 2 address documents (which are set above as orderAddress and sentFromAddress) and for the cart I decided to get the data when needed for every cart item.
So the method which I put in the update is not the final one, but I want to understand what is happening up there as I do not understand why:
Why the cart is shown as empty if I do a print(order); right before the return and in the bloc it has data;
Why the orderAddress and sentFromAddress are both empty no matter what I try;
To be short: You'll never be able to get a List synchronously if you get the data async from firebase.
Both questions have the same answer:
Your timeline:
For each document
Create an empty list
Initiate the firebase query - getDocuments()
Subscribe to the returned future with - .then((snapshot){cart.add(...)}).
This lambda will be invoked when the documents arrived.
Another subscribe
Save your empty cart and first/last of empty addresses to an OrderModel
Your List contains the references to your empty lists indirectly
Use your bloc, some time elapsed
Firebase done, your callbacks starts to fill up your lists.
Regarding your comment like stream.listen doesn't like async callbacks:
That's not true, you just have to know how async functions work. They're run synchronously until the first await, then return with an incomplete future. If you do real async things you have to deal with the consequences of the time delay like changed environment or parallel running listeners.
You can deal with parallel running with await for (T v in stream) or you can use subscription.pause() and resume.
If anything returns a future, just do this:
...getDocuments().then((value) => {
value is the item return here. Do something with it....
})
Also, you might want to split your method up a bit to share the responsibility.
If getDocuments is a Future function and you need to wait for it, I think you should add await before it. I also don't see the snapshot status checking in your code pasted here. Maybe you have already checked the snapshot status in other function? Make sure the snapshot is ready when using it.