i'm trying to show a list of data coming from the cloud firestore, and applied a filter to get this data by month. Everything works pretty fine unless once another user logs in, the data received afterwards is his data and not of the actual logged in user. Once I remove the app from the background it gets me data correctly at that time.
After some research, I realized that it has to do with cache data. I tried adding the following lines to my code:
Logout function:
await FirebaseFirestore.instance.clearPersistence();
await FirebaseFirestore.instance.disableNetwork();
Login function:
await FirebaseFirestore.instance.enableNetwork();
But nothing actually changes, is there anything I can edit/add to solve this?
Here is the code of full functions:
Future<void> loginWithEmailAndPassword(String email, String password) async {
await FirebaseFirestore.instance.enableNetwork();
UserCredential userCredential =
await auth.signInWithEmailAndPassword(email: email, password: password);
}
Future<void> logOut() async {
await FirebaseFirestore.instance.clearPersistence();
await FirebaseFirestore.instance.disableNetwork();
await auth.signOut();
}
}
Edited my code and now getting this error:
FirebaseException ([cloud_firestore/failed-precondition] Operation was rejected because the system is not in a state required for the operation's execution. If performing a query, ensure it has been indexed via the Firebase console.)
Related
Actually I'm trying to get current user id after authentication but I don't know how to do it. While registering email/password authentication only stores email, password as well as uid. I tried to fetch that uid by calling following function after pressing login button but it return null. I am not able to get the uid of current user.
Calling a function after pressing login button:
final FirebaseAuth auth = FirebaseAuth.instance;
Future<void> inputData() async {
final User? user = await auth.currentUser;
final uid = user?.uid;
// here you write the codes to input the data into firestore
print("User id: ${uid}");
}
You can see in the console it prints null:
Most likely your inputData function runs before the user is signed in. To ensure your code can properly react to when the user is signed in or out, use an auth state listener as shown in the first snippet in the documentation on getting the current user.
Future<void> inputData() async {
FirebaseAuth.instance
.authStateChanges()
.listen((User? user) {
if (user != null) {
print(user.uid);
}
});
}
one reason why you might be getting null is you're not awaiting the getToken function, so make it a future and await it, I'm sure you'll get the token provided the user is authenticated.
I'm using Flutter and Firebase for my app and the following is the code for my register function:
Future registerWithEmailAndPassword(String email, String name, String password) async {
try{
// Creates user account with Firebase Auth:
UserCredential result = await _auth.createUserWithEmailAndPassword(email: email, password: password);
User user = result.user!;
// Creates a new document in Firestore with the uid:
await DatabaseService(uid: user.uid).createUserData(
name: name,
email: email,
);
return _userObjectFromUser(user);
} on FirebaseAuthException catch(e) {
return e;
}
}
It works well. However, I keep wondering if this is the best way to do this... What if the connection gets interrupted after creating the account but before creating the documents in Firestore? What if the creation of the document fails for some reason? Then the user would be in a weird situation where they have an account but no data saved in the database, meaning the app would probably load forever.
So, I wonder: is there a way to create something similar to a batch write that would somehow create an account at the same time as the documents are created?
I guess you shouldn't be concerned about this since the two methods will run on each other, they're in a really small chance of this happening, either both will succeed or both will fail together, however, I can recommend for those cases to listen to the authStateChanges() stream and take an action based on it, combined with using the isNew like this :
// At first, we were not sure that the document exists
bool areWeSureThatTheuserHaveDocument = false;
// we listen to auth changes of new user
FirebaseAuth.instance.authStateChanges().listen((user) {
// we want this method to get triggered only when the user authenticates, we don't want it to get executed when the user signs out
if(user != null && !areWeSureThatTheuserHaveDocument) {
// here we create the document
await DatabaseService(uid: user.uid).createUserData(
name: name,
email: email,
);
// now if the document does exists, it will return true, for future checks on this method it will not be executed
areWeSureThatTheuserHaveDocument = await doesUserDocumentExists(user.uid);
}
});
// this is the check document existence
Future<bool> doesUserDocumentExists(String id) async {
final collection = await FirebaseFirestore.instance.collection("users").get();
return collection.docs.map((doc) => doc.id).contains(id);
}
Actually, if you're willing to implement this code or something similar to it, you might want to know that by this you can make sure 100% of the user has a document in the database, but it will cost you one additional read to check that existence od document.
Since you tagged with google-cloud-functions, doing the create-user-and-write-profile-document would reduce the chances of having the type of interruption that you talk about.
But my approach is typically to either write the profile document each time the onAuthState changed listener for a user gets a value, or to check for the existence of a document at that time and create it if needed.
I developed an Agro App and I have enabled offline persistence because, after logging in, operators can spend 3-4 hours recording information offline until they return to the central point where there is internet.
The problem I have is that sometimes the application asks for credentials (login and password) again when they do not have internet, which means that they cannot record information offline or they have to go back to the central point to authenticate which is not efficient.
I understand that the Firebase auth token lasts up to an hour, how can I extend this time?
The way I am authenticating users using a provider is like following:
class UsuarioProvider {
final FirebaseAuth _firebaseAuth;
DatabaseReference db = FirebaseDatabase.instance.reference();
UsuarioProvider({FirebaseAuth firebaseAuth})
: _firebaseAuth = firebaseAuth ?? FirebaseAuth.instance;
Future <Map<String, dynamic>> signIn(String email, String password) async {
try {
UserCredential result = await FirebaseAuth.instance.signInWithEmailAndPassword(email: email, password: password);
User user = result.user;
final token = await user.getIdToken();
return {'ok' : true, 'token' : token, 'localId': user.uid, 'email' : user.email};
} catch (e) {
print(e);
return {'ok': false, 'code': '${e.code}', 'mensaje': '${e.message}' };
}
}
How can I increase the Firebase Token Time? or How can I prevent users from being prompted for credentials when they are offline?
UPDATE:
The way I'm using to call firebase is:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
final prefs = new PreferenciasUsuario();
await prefs.initPrefs();
final pushNotificationsProvider = new PushNotificationsProvider();
pushNotificationsProvider.initNotifications();
FirebaseDatabase database;
database = FirebaseDatabase.instance;
database.setPersistenceEnabled(true);
database.setPersistenceCacheSizeBytes(10000000); // 10MB de cache
runApp(MyApp());
}
Firebase Authentication uses two types of tokens: a refresh token that "never" expires, and a short-lived ID token that is valid for an hour but is auto-refreshed by the SDK. There is no way to expand the life-time of the ID tokens.
If you want to pass a value along to a client that they can use for a longer period of time, you can mint an authentication cookie, which can be valid for up to two weeks. Note that this is a sensitive operation, so it can only be performed in trusted environments (typically by using the Firebase Admin SDK). For a full walkthrough of the process, see the Firebase documentation on Managing Session Cookies.
I'm having some problems with a line in the function below. The function is handling async gets from Firebase Storage. I'm using it to get the names and urls of files I have stored there.
The issues is with getting the Urls. Specifically on the line:
String url = element.getDownloadURL().toString();
getDownloadedURL() is a Firebase future. I tried to await it, but it won't recognise the await, I guess due to "element".
The over all effect is that when I'm using this in my UI via a Future builder, the name comes out fine but the Url doesn't. It is being retrieved as the print statement shows it. But it's not being waited for, so the UI is already updated.
Been trying lots of things, but haven't found a solution, so any help would be greatly appreciated.
Future<void> getImageData() async {
final imagesFromStorage = await fb
.storage()
.refFromURL('gs://little-big-deals.appspot.com')
.child('images')
.listAll();
imagesFromStorage.items.forEach((element) {
print(element.name);
String url = element.getDownloadURL().toString();
print(url.toString());
imageData.add(ImageData(element.name, url.toString()));
});
}
Many thanks
You can't use async in forEach.
Just use a for loop:
Future<void> getImageData() async {
final imagesFromStorage = await fb
.storage()
.refFromURL('gs://little-big-deals.appspot.com')
.child('images')
.listAll();
for (var element in imagesFromStorage.items) {
print(element.name);
String url = (await element.getDownloadURL()).toString();
print(url.toString());
imageData.add(ImageData(element.name, url.toString()));
}
}
I want each user to see his/her data in real time. The app requires
the user data to change in real time. The code I wrote is only
showing the data of the first user in the firestore collection. When
a second logs in, the details of the first user is shown, instead of
the second user. Here is my code:
_getTapCount(){ Firestore.instance.collection('user_collection').where('userId',
isEqualTo: _uid).snapshots().listen((event) {
if(event.documents.isNotEmpty){
var mydata = event.documents[0].data;
setState(() {
_tapCount = mydata['tapCount'];
});
} }); }
To become the data from the current logged in user you can use the following code as an example.
FirebaseUser currentUser = await FirebaseAuth.instance.currentUser();
var userData = await Firestore.instance.collection("user_collection").document(currentUser.uid).get();
print(userData['userDataFieldYouWant']);
With the currentUser() method you become the current logged in user. After this you can become the uid from the FirebaseUser object (here currentUser) and then you can access the data from the user with the get() method.