firebase not creating data when user signs for first time - flutter

when new user signIn, In firebase Authentication tab it is showing users details.
but not adding the function to firestore.
google signIn code:
Future<UserCredential> signInWithGoogle() async {
showDialog(
context: context,
builder: (context) {
return const Center(child: CircularProgressIndicator());
});
final _auth = FirebaseAuth.instance;
// Trigger the authentication flow
final GoogleSignInAccount? googleUser = await GoogleSignIn().signIn();
// Obtain the auth details from the request
final GoogleSignInAuthentication? googleAuth =
await googleUser?.authentication;
// Create a new credential
final credential = GoogleAuthProvider.credential(
accessToken: googleAuth?.accessToken,
idToken: googleAuth?.idToken,
);
final UserCredential authResult =
await _auth.signInWithCredential(credential);
final user = authResult.user;
if (authResult.additionalUserInfo!.isNewUser) {
if (user != null) {
await DbServices().addNewUser(FirebaseAuth.instance.currentUser!.uid);
}
}
removeLoading();
// Once signed in, return the UserCredential
return await FirebaseAuth.instance.signInWithCredential(credential);
}
removeLoading() {
return Navigator.of(context).pop();
}
}
**this is addNewUser function:**
Future addNewUser(useruid) async {
int heartid = await updateHeartId();
print('updated Heart Id $heartid....................');
await usersCollection.doc(useruid).set({
'user name': FirebaseAuth.instance.currentUser!.displayName,
'uid': useruid,
'heartId': heartid,
'total hearts': 0,
'ringed': false,
'Timestamp': FieldValue.serverTimestamp(),
'profile pic': '',
'todayRinged': false,
'ringedDay': DateTime.now().day,
});
}
when I looked into it, firebase isn't adding the function addNewUser in firestore.
I don't understand what's happening here.
when I signIn for first time the app crashes saying :
> StateError(Bad state: cannot get a field on a DocumentSnapshotPlatform
> which does not exist)
why is the addNewUser function not adding to firestore?

Related

How to save the user email and name in database while signing in with google?

I am using google auth and i want to save user data in database...How can i get user details here and save it to database?
Future<void> signUpWithGoogle(
BuildContext context,
bool mounted,
) async {
final googleSignIn = GoogleSignIn();
final googleUser = await googleSignIn.signIn();
if (googleUser == null) return;
final googleAuth = await googleUser.authentication;
final credential = GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
try {
await FirebaseAuth.instance.signInWithCredential(credential);
FirebaseFirestore.instance
.collection('users')
.doc(FirebaseAuth.instance.currentUser!.uid)
.set({
//
});
if (mounted) return;
InfoBox.show(context, InfoBox.success, "Signed In With Google");
} on Exception catch (e) {
if (mounted) return;
InfoBox.show(context, InfoBox.success, e.toString());
}
}
Please modify the code under your try block as below
try {
final authResult = await FirebaseAuth.instance.signInWithCredential(credential);
if (authResult.additionalUserInfo!.isNewUser) {
await FirebaseFirestore.instance
.collection('Users')
.doc(authResult.user!.uid)
.set({
"Name": authResult.user!.displayName,
"Email": authResult.user!.email,
});
}
if (mounted) return;
InfoBox.show(context, InfoBox.success, "Signed In With Google");
} on Exception catch (e) {
if (mounted) return;
InfoBox.show(context, InfoBox.success, e.toString());
}
Please accept the solution if it solves your problem
Inside your try block, you have to get UserCredential which gives the User object data to be saved in the next line.
try{
UserCredential userCredential =
await FirebaseAuth.instance.signInWithCredential(credential);
User user = userCredential.user; // User is updated version of FirebaseUser
FirebaseFirestore.instance.collection('users').doc(user.uid).set(/*Add Mapped User object*/);}
You can use user object to get all the necessary data.

Flutter Google Sign In error after canceling Choose an account pop-up

After clicking sign in, choose an account pop comes up but if the user pressed back or outside of the pop-up it throws an error.
Error -
Sign in class -
class GoogleSignInProvider extends ChangeNotifier {
final googleSignIn = GoogleSignIn();
GoogleSignInAccount? _user;
GoogleSignInAccount get user => _user!;
Future googleLogin() async {
await googleSignIn.signOut();
final googleUser = await googleSignIn.signIn();
if (googleUser == null) return;
_user = googleUser;
final googleAuth = await googleUser.authentication;
final credential = GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken, idToken: googleAuth.idToken);
await FirebaseAuth.instance.signInWithCredential(credential);
notifyListeners();
}
}
Sign out button -
TextButton(
onPressed: () async {
final googleCurrentUser = GoogleSignIn().currentUser;
if (googleCurrentUser != null) {
await GoogleSignIn().disconnect();
}
FirebaseAuth.instance.signOut();
Navigator.pop(context);
},
child: const Text(
'Yes',
style: TextStyle(color: primaryColor),
))
Instead of if (googleUser == null) return; try using if (googleUser == null) return null;.
Here is my sign in method;
Future<UserCredential?> signInWithGoogle() async {
final _googleSignIn = GoogleSignIn();
await _googleSignIn.disconnect().catchError((e, stack) {
print(e);
});
final GoogleSignInAccount? googleUser = await _googleSignIn.signIn();
// handling the exception when cancel sign in
if (googleUser == null) return null;
// Obtain the auth details from the request
final GoogleSignInAuthentication? googleAuth =
await googleUser.authentication;
// Create a new credential
final credential = GoogleAuthProvider.credential(
accessToken: googleAuth?.accessToken,
idToken: googleAuth?.idToken,
);
return await FirebaseAuth.instance.signInWithCredential(credential);
}
The sign out method;
Future signOut() async {
var result = await FirebaseAuth.instance.signOut();
return result;
}
This only happens in debugging, in release this won't be an issue.

Flutter - Google Sign In uses previously signed in account (user) even after signing out and signing in with another account

When I hit "Sign in with Google", it does let me choose the account although somehow, the previous user gets loaded.
class GoogleSignInProvider extends ChangeNotifier {
GoogleSignIn? _googleSignIn;
GoogleSignInAccount? _user;
GoogleSignInAccount get user => _user!;
bool _isGoogleLogin = false;
final _auth = FirebaseAuth.instance;
Future signInWithGoogle(context) async {
try {
_googleSignIn = GoogleSignIn();
final googleUser = await _googleSignIn!.signIn();
if (googleUser == null) {
return;
}
_user = googleUser;
final googleAuth = await googleUser.authentication;
final AuthCredential credential = GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
var authCredential = await _auth.signInWithCredential(credential);
_isGoogleLogin = true;
if (authCredential.user != null) {
final snapshot = await FirebaseFirestore.instance
.collection('users')
.doc(authCredential.user!.uid)
.get();
if (!snapshot.exists) {
var userId = authCredential.user!.uid;
FirebaseFirestore.instance.collection('users').doc(userId).set({
"email": googleUser.email,
"workoutsCount": 0,
"lastWorkoutDate": null
});
}
}
notifyListeners();
Navigator.of(context).pushNamed('/dashboard');
} on FirebaseAuthException catch (e) {
showSnackBar(context, e);
} catch (e) {
print('ERROR = ');
print(e);
}
}
Future<void> signOut(context) async {
try {
if (_isGoogleLogin) {
final googleCurrentUser =
GoogleSignIn().currentUser ?? await GoogleSignIn().signIn();
if (googleCurrentUser != null) {
await GoogleSignIn().disconnect().catchError((e, stack) {
FirebaseCrashlytics.instance.recordError(e, stack);
});
}
}
await _auth.signOut();
} catch (e) {
showSnackBar(context, e);
} finally {
_isGoogleLogin = false;
Navigator.of(context).pushNamedAndRemoveUntil(
'/register_login', (Route<dynamic> route) => false);
}
}
Is there something wrong with this code??
Can you try to disconnect the account before signing out:
GoogleSignIn _googleSignIn = GoogleSignIn();
await _googleSignIn.disconnect();
await FirebaseAuth.instance.signOut();
You are using firebase auth then please use thsi for signout user :
How to Signout a user in Flutter with Firebase authentication
Remove if (_isGoogleLogin) from your signOut function. googleCurrentUser will check the consitionn and responses based on the result.
First Create a Global variable
FirebaseAuth firebaseAuth = FirebaseAuth.instance;
GoogleSignIn googleSignIn = GoogleSignIn();
Make sure you are using the above variable for login and logout
final googleCurrentUser = firebaseAuth.currentUser;
if (googleCurrentUser != null) {
googleSignIn.signOut();
await firebaseAuth.signOut();
}
User above code for signout user

adding authenticated user to firestore 'users' collection flutter

I am stuck at the adding an authenticated user to a firestore 'users' collection.
Unhandled Exception: [cloud_firestore/not-found] Some requested document
was not found.
User signs in via Google:
class GoogleSignInProvider extends ChangeNotifier {
final GoogleSignIn _googleSignIn = GoogleSignIn();
GoogleSignInAccount _user;
GoogleSignInAccount get user => _user;
AuthService auth = AuthService();
Future googleSignIn(BuildContext context) async {
try {
final googleUser = await _googleSignIn.signIn();
if (googleUser == null) return;
_user = googleUser;
final googleAuth = await googleUser.authentication;
final AuthCredential credential = GoogleAuthProvider.credential(
idToken: googleAuth.idToken, accessToken: googleAuth.accessToken);
await FirebaseAuth.instance.signInWithCredential(credential);
} catch (e) {
print(e.toString());
}
final User currentUser = FirebaseAuth.instance.currentUser;
if (currentUser != null)
usersRef.add(currentUser.uid);
Navigator.of(context).pushReplacement(
CupertinoPageRoute(builder: (_) => TabScreen()));
notifyListeners();
}
However, no matter what and how I tried the authentication firebase id is not added to the usersRef (the firestore collection). How do I fix it?
My firestore rules are:
match /users/{userId}/{documents=**} {
allow read: if request.auth.uid != null;
allow write, update, create, delete: if isOwner(userId);
}
function isOwner(userId) {
return request.auth.uid == userId;
}
Help appreciated very much!
so this solved my issue:
final User currentUser = FirebaseAuth.instance.currentUser;
if (currentUser != null)
await usersRef.doc(currentUser.uid).set({'email':
user.email, 'username': user.displayName, 'photoUrl':
user.photoURL});
I think, I was overthinking it..
Thanks, ppl for the directions and help!
Do this.
Map data = {};
FirebaseFirestore.instance
.collection('usersCollection')
.doc(currentUser.uid)
.set(data);
in place of
usersRef.add(currentUser.uid);
Use set when you have doc id and use add when you want an auto generated id.

What to do after login but before essential user data gets returned in flutter

I have a flutter app that uses firebase for authentication.
return StreamBuilder<FirebaseUser>(
stream: FirebaseAuth.instance.onAuthStateChanged,
builder: (BuildContext context, snapshot) {
if (snapshot.hasData) {
return HomeScreen();
} else {
return LoginScreen();
}
},
);
so basically as soon as user authenticates, this will take to the home screen. but i dont want that, i want to wait on another piece of data from my api, say onboarded, if onboarded == true then HomeScreen otherwise OnboardingScreen.
So the tricky part is before that data comes in, i want to stay on the login screen. how do i have the user stay on the LoginScreen? it seems the best way is to have another stream listen to the onboardedLoading and combine these 2 streams?
Make a dart file auth.dart, in that, paste this line of code,
final FirebaseAuth auth = FirebaseAuth.instance;
Future<FirebaseUser> handleSignInEmail(String email, String password) async {
AuthResult result = await auth.signInWithEmailAndPassword(email: email, password: password);
final FirebaseUser user = result.user;
assert(user != null);
assert(await user.getIdToken() != null);
final FirebaseUser currentUser = await auth.currentUser();
assert(user.uid == currentUser.uid);
print('signInEmail succeeded: $user');
return user;
}
Future<FirebaseUser> handleSignUp(email, password) async {
AuthResult result = await auth.createUserWithEmailAndPassword(email: email, password: password);
final FirebaseUser user = result.user;
assert (user != null);
assert (await user.getIdToken() != null);
return user;
}
In your login/ Sigup page, create an instance of my auth class:
var authHandler = new Auth();
In the onPressed () of your button
onPressed: () {
if(onboardedLoading==true){
authHandler.handleSignInEmail(emailController.text, passwordController.text)
.then((FirebaseUser user) {
Navigator.push(context, new MaterialPageRoute(builder: (context) => HomeScreen()));
}).catchError((e) => print(e));
}
}else{
//Show An Animation, such as CirclularProgressIndicator.
}
You can design a simple loading screen, then use Navigator.pushAndRemoveUntil() to whichever screen you need after getting AuthState.