Flutter Getx: google signin and map data to firebase automatically logs me back in as same user? - flutter

I am trying to login with google and have the data mapped to a firebase user. I'm using getX. So far this works HOWEVER it automatically logs me back in as the same user if I logout and then try to log back in again. I'll send the code for my login page and the page where the logout button is if needed, but I suspect this may have to do with my AuthController which I've included here
class AuthController extends GetxController {
static AuthController instance = Get.find();
GoogleSignIn googleSignIn = GoogleSignIn();
Rxn<User> firebaseUser = Rxn<User>();
Rxn<UserModel> firestoreUser = Rxn<UserModel>();
final RxBool admin = false.obs;
String usersCollection = "users";
#override
void onReady() async {
//run every time auth state changes
ever(firebaseUser, handleAuthChanged);
firebaseUser.bindStream(user);
super.onReady();
}
handleAuthChanged(firebaseUser) async {
//get user data from firestore
if (firebaseUser?.uid != null) {
firestoreUser.bindStream(streamFirestoreUser());
print("You are logged in as ${firebaseUser.email}");
await isAdmin();
}
//this is for new users
if (firebaseUser == null) {
print('Send to signin');
Get.offAll(LoginPage());
} else {
Get.offAll(AppSetup());
}
}
// Firebase user one-time fetch
Future<User> get getUser async => auth.currentUser!;
// Firebase user a realtime stream
Stream<User?> get user => auth.authStateChanges();
//Streams the firestore user from the firestore collection
Stream<UserModel> streamFirestoreUser() {
print('streamFirestoreUser()');
return firebaseFirestore
.doc('/users/${firebaseUser.value!.uid}')
.snapshots()
.map((snapshot) => UserModel.fromSnapshot(snapshot));
}
//get the firestore user from the firestore collection
Future<UserModel> getFirestoreUser() {
return firebaseFirestore
.doc('/users/${firebaseUser.value!.uid}')
.get()
.then((documentSnapshot) => UserModel.fromSnapshot(documentSnapshot));
}
//Method to handle user sign in using email and password
// User registration using email and password
googleLogin(BuildContext context) async {
final GoogleSignInAccount? googleUser = await googleSignIn.signIn();
if (googleUser != null) {
final googleAuth = await googleUser.authentication;
if (googleAuth.accessToken != null && googleAuth.idToken != null) {
try {
await auth
.signInWithCredential(
GoogleAuthProvider.credential(
idToken: googleAuth.idToken,
accessToken: googleAuth.accessToken),
)
.then((firebaseUser) async {
print('uid: ' + firebaseUser.user!.uid.toString());
print('email: ' + firebaseUser.user!.email.toString());
//create the new user object from the login modelled data
UserModel _newUser = UserModel(
id: firebaseUser.user!.uid,
email: firebaseUser.user!.email!,
name: firebaseUser.user!.email!,
photoURL: firebaseUser.user!.photoURL,
cart: [],
);
//create the user in firestore here with the _addUserToFirestore function
_updateUserFirestore(_newUser, firebaseUser.user!);
});
} on FirebaseAuthException catch (error) {
Get.snackbar('auth.signUpErrorTitle'.tr, error.message!,
snackPosition: SnackPosition.BOTTOM,
duration: Duration(seconds: 10),
backgroundColor: Get.theme.snackBarTheme.backgroundColor,
colorText: Get.theme.snackBarTheme.actionTextColor);
}
}
}
}
void _updateUserFirestore(UserModel user, User _firebaseUser) {
firebaseFirestore.doc('/users/${_firebaseUser.uid}').update(user.toJson());
update();
}
updateUserData(Map<String, dynamic> data) {
logger.i("UPDATED");
firebaseFirestore
.collection(usersCollection)
.doc(firebaseUser.value!.uid)
.update(data);
}
//check if user is an admin user
isAdmin() async {
await getUser.then((user) async {
DocumentSnapshot adminRef =
await firebaseFirestore.collection('admin').doc(user.uid).get();
if (adminRef.exists) {
admin.value = true;
} else {
admin.value = false;
}
update();
});
}
// This is the proper sign out method!
Future<void> signOut() {
return auth.signOut();
}
}

Simply add this line of code into your logout function
> await googleSignIn.signOut()

Related

How to pass uid and email to sharedpreference method When signUp in flutter?

I built the google signin and email password signup app using flutter,
I want when user log to application then user's "uid" and "email" save in shared preference.
In google login I built when login then pass the boolean value and pass uid and email to shared preference. Then when users close app and when reopen then login in home screen and can get uid and email.
But when user signup or login using email password then can not pass the boolean value and uid and email to google login shared preference. In below I'll mentioned google login method and email password method.
googlelogin method
// handling google sigin in
Future handleGoogleSignIn() async {
final sp = context.read<SignInProvider>();
final ip = context.read<InternetProvider>();
await ip.checkInternetConnection();
if (ip.hasInternet == false) {
openSnackbar(context, 'Check your Internet connection', Colors.red);
googleController.reset();
} else {
await sp.signInWithGoogle().then((value) {
if (sp.hasError == true) {
openSnackbar(context, sp.errorCode.toString(), Colors.white);
googleController.reset();
} else {
// checking whether user exists or not
sp.checkUserExists().then((value) async {
if (value == true) {
// user exists
await sp.getUserDataFromFirestore(sp.uid).then((value) => sp
.saveDataToSharedPreferences()
.then((value) => sp.setSignIn().then((value) {
googleController.success();
handleAfterSignIn();
})));
} else {
// user does not exist
sp.saveDataToFirestore().then((value) => sp
.saveDataToSharedPreferences()
.then((value) => sp.setSignIn().then((value) {
googleController.success();
handleAfterSignIn();
})));
}
});
}
});
}
//login function
}
saveDataToSharedPreferences method and setSignIn boolean value (it has in a siginprovider class in another file)
class SignInProvider extends ChangeNotifier {
//instantiate of firebaseAuth, facebook and google
final FirebaseAuth firebaseAuth = FirebaseAuth.instance;
final GoogleSignIn googleSignIn = GoogleSignIn();
bool _isSignedIn = false;
bool get isSignedIn => _isSignedIn;
//hasError, errorCode, provider,uid,email,name,imageUrl
bool _hasError = false;
bool get hasError => _hasError;
String? _errorCode;
String? get errorCode => _errorCode;
String? _uid;
String? get uid => _uid;
String? _email;
String? get email => _email;
SignInProvider() {
checkSignInUser();
}
Future checkSignInUser() async {
final SharedPreferences s = await SharedPreferences.getInstance();
_isSignedIn = s.getBool('signed_in') ?? false;
notifyListeners();
}
Future setSignIn() async {
final SharedPreferences s = await SharedPreferences.getInstance();
s.setBool('signed_in', true);
_isSignedIn = true;
notifyListeners();
}
Future saveDataToSharedPreferences() async {
final SharedPreferences s = await SharedPreferences.getInstance();
await s.setString('email', _email!);
await s.setString('uid', _uid!);
notifyListeners();
}
Future getDataFromSharedPreferences() async {
final SharedPreferences s = await SharedPreferences.getInstance();
_email = s.getString('email');
_uid = s.getString('uid');
notifyListeners();
}
}
**email password signup**
void signUp(String email, String password) async {
if (_formkey.currentState!.validate()) {
await _auth
.createUserWithEmailAndPassword(email: email, password: password)
.saveDataToSharedPreferences()
.sp.setSignIn()
.then((value) => {postDetailsToFirestore()})
.catchError((e) {
Fluttertoast.showToast(msg: e!.message);
});
}
}
in email password signup has postDetailsToFirestore method
postDetailsToFirestore() async {
// calling our fireStore
//calling our user model
// sending these values
FirebaseFirestore firebaseFirestore = FirebaseFirestore.instance;
User? user = _auth.currentUser;
UserModel userModel = UserModel();
if (user != null) {
//writing all the values
userModel.email = user?.email;
userModel.uid = user?.uid;
await firebaseFirestore
.collection("users")
.doc(user?.uid)
.set(userModel.toMap());
Fluttertoast.showToast(msg: "Account created successfully ");
Navigator.pushAndRemoveUntil(
(context),
MaterialPageRoute(builder: (context) => HomeScreen()),
(route) => false);
}
}
in email password signup I called
these 2 methods but show this error
when signup how to pass uid and emaill to saveDataToSharedPreferences method and pass sp.setSignIn() boolean like as in google signing?
You are trying to use method saveDataToSharedPreferences from Future class, not from auth. For fix this issue, call prefs from main object:
final result = await _auth.createUserWithEmailAndPassword(
email: email,
password: password,
);
result.saveDataToSharedPreferences()
.sp.setSignIn()
// etc.

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

Fetch data from firestore after Sign In before showing HomeScreen

When a User sign up for the first time, i want that he gets a own firestore document with some data. This data I want to show on the homescreen but I get an error that the data is not there yet. After hot reload the data is there so the problem is that the homescreen is shown before the data is fetched from firestore although I use a FutureBuilder. I only got this problem when a user signs in for the first time.
class GoogleSignInProvider extends ChangeNotifier {
final googleSignIn = GoogleSignIn();
GoogleSignInAccount _user;
GoogleSignInAccount get user => _user;
Future googleLogin() async {
try {
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);
await getUserData();
} catch (e) {
print(e.toString());
}
notifyListeners();
}
Future getUserData() async {
final myUser = FirebaseAuth.instance.currentUser;
bool userExist = await checkIfUserExists(myUser.uid);
if (userExist == false) {
print('User dont exist');
await FirebaseFirestore.instance.collection('users').doc(myUser.uid).set({
"email": myUser.email,
"plans": [],
"userScore": "100",
});
} else {
print('User exist');
}
/// Save userData from firestore in a Helper class which is shown on the homescreen
var userData = FirebaseFirestore.instance.collection('users').doc(myUser.uid);
return FutureBuilder(
future: userData.get(),
builder: (context, userDataSnapshot) {
if (userDataSnapshot.data == ConnectionState.done) {
var value = userDataSnapshot.data;
UserManager.userdata = value.data(); //static class where userData is stored
return null;
} else {
return Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}
});
}
Future<bool> checkIfUserExists(String uid) async {
try {
var collectionRef = FirebaseFirestore.instance.collection('users');
var doc = await collectionRef.doc(uid).get();
return doc.exists;
} catch (e) {
throw e;
}
}
EDIT
This is the Button where the user can sign In
ElevatedButton.icon(
onPressed: () {
final provider = Provider.of<GoogleSignInProvider>(context, listen: false);
provider.googleLogin();
},
icon: Icon(MdiIcons.google),
label: Text(
'Sign In with Google',
style: TextStyle(fontSize: 16),
),
),
The problem here is that FutureBuilder is a widget, it should not be used in a function to wait for a future to complete, but in another widget to have callbacks on the completion and change display based on that.
If not rendered, FutureBuilder will do nothing but be instantiated and occupy memory.
You should probably modify your code as such:
...
/// Save userData from firestore in a Helper class which is shown on the homescreen
var userData = await FirebaseFirestore.instance.collection('users').doc(myUser.uid).get();
UserManager.userdata = userData.data();
...
Should you want to add a CircularProgress on your main screen, this would be done by lisntening to your Provider in some way.

Firebase documentation for flutter does not work for deleting user

The following documentation on deleting a user does not work:
try {
await FirebaseAuth.instance.currentUser.delete();
} catch on FirebaseAuthException (e) {
if (e.code == 'requires-recent-login') {
print('The user must reauthenticate before this operation can be executed.');
}
}
"delete()" is not a function recognized by Flutter. "FirebaseAuthException" is also not recognized by Flutter.
How do I delete a user? Where do I find this information?
Using flutter, if you want to delete firebase accounts together with the associated firestore user collection document, the following method works fine. (documents in user collection named by the firebase uid).
Database Class
class DatabaseService {
final String uid;
DatabaseService({this.uid});
final CollectionReference userCollection =
Firestore.instance.collection('users');
Future deleteuser() {
return userCollection.document(uid).delete();
}
}
Use Firebase version 0.15.0 or above otherwise, Firebase reauthenticateWithCredential() method throw an error like { noSuchMethod: was called on null }.
Authentication Class
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
Future deleteUser(String email, String password) async {
try {
FirebaseUser user = await _auth.currentUser();
AuthCredential credentials =
EmailAuthProvider.getCredential(email: email, password: password);
print(user);
AuthResult result = await user.reauthenticateWithCredential(credentials);
await DatabaseService(uid: result.user.uid)
.deleteuser(); // called from database class
await result.user.delete();
return true;
} catch (e) {
print(e.toString());
return null;
}
}
}
Then use the following code inside the clickable event of a flutter widget tree to achieve the goal:
onTap: () async {
await AuthService().deleteUser(email, password);
}

Flutter, firebase. I want to show my registration form data other than the email and password and connect to a unique uid

Register Screen On Pressed method given, I believe there is a problem with calling Firebase user = result.user
onPressed: () async {
if(_formKey.currentState.validate()){
setState(() => loading = true);
dynamic result = await _auth.registerWithEmailAndPassword(email, password);
FirebaseUser user = result.user;
await DatabaseService(uid: user.uid).newUserInfo(
_nameC.text,
_cityC.text,
_contactnoC.toString()
);
if(result == null) {
setState(() {
error = 'Please supply a valid email';
loading = false;
});
}}},
// Database backend
class DatabaseService {
final String uid;
DatabaseService ({this.uid});
final CollectionReference userdata2 = Firestore.instance.collection('UserData');
Future newUserInfo(String name, String city, String contactno) async {
return await userdata2.document(uid).setData({
'name' : name,
'city' : city,
'contactno' : contactno
});
}}
// authentication backend
// register with email and password
Future registerWithEmailAndPassword(String email, String password) async {
try {
AuthResult result = await _auth.createUserWithEmailAndPassword(email: email, password: password);
FirebaseUser user = result.user;
DatabaseService(uid: user.uid);
return _userFromFirebaseUser(user);
} catch (error) {
print(error.toString());
return null;
} }
// user.dart
class User {
final String uid;
User({this.uid});
}