Flutter Firebase Authentication and/or Login is not Successful - firebase

Firebase
Authentication: email/Password was already set to Enabled.
Realtime Database: users records exist.
Email and Password: thoroughly checked and verified correct.
below are the code for this issue;
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
void loginAndAuthenticateUser(BuildContext context) async
{
final User? firebaseUser = (await _firebaseAuth
.signInWithEmailAndPassword(
email: emailTextEditingController.text,
password: passwordTextEditingController.text
).catchError((errMsg){
displayToastMessage("Error: " + errMsg.toString(), context);
})).user;
if(firebaseUser !=null)
{
userRef.child(firebaseUser.uid).once().then((value) => (DataSnapshot snap){
if(snap.value != null){
Navigator.pushNamedAndRemoveUntil(context, MainScreen.idScreen, (route) => false);
displayToastMessage("Login successful", context);
}else{
_firebaseAuth.signOut();
displayToastMessage("No records exist. Please create new account", context);
}
});
}else{
displayToastMessage("Error: Cannot be signed in", context);
}
}

Try to use await with catch/try, instead of mixing with the .then/.catchError syntax (I don't know what's exactly in userRef, depending on it the database query might need to be adjusted):
try {
UserCredential userCredential = await _firebaseAuth
.signInWithEmailAndPassword(
email: emailTextEditingController.text,
password: passwordTextEditingController.text);
final User? firebaseUser = userCredential.user;
if (firebaseUser != null) {
final DatabaseEvent event = await
userRef.child(firebaseUser.uid).once();
if (event.snapshot.value != null) {
Navigator.pushNamedAndRemoveUntil(context, MainScreen.idScreen,
(route) => false);
displayToastMessage("Login successful", context);
} else {
displayToastMessage("No records exist. Please create new account",
context);
await _firebaseAuth.signOut();
}
} else {
displayToastMessage("Error: Cannot be signed in", context);
}
} catch(e) {
// handle error
}

Related

I have some problems in Flutter Firebase Login

I am coding an app for my company and I tried to add firebase authentication for login and registration to my app. The app shows no error and runs successfully.
But when a user tries to login with the wrong email and password, it is showing an internal flutter error instead of the toast I have programmed. And also I have used shared preferences to make users stay logged in.
So when a user tried to log in with the wrong credential it is showing an internal flutter error and when the app is re-opened, instead of going to the login screen, it is using the wrong credential and navigates user to Home Screen which is ridiculous.
These are the declared variables:
final FirebaseAuth firebaseAuth = FirebaseAuth.instance;
final _formKey = GlobalKey<FormState>();
TextEditingController _emailcontroller = TextEditingController();
TextEditingController _passwordcontroller = TextEditingController();
bool passvis = true;
bool loading = false;
And this is the function for login:
Future loginForm() async {
FormState formSate = _formKey.currentState;
if (formSate.validate()) {
final User firebaseUser = (await firebaseAuth
.signInWithEmailAndPassword(
email: _emailcontroller.text,
password: _passwordcontroller.text)
.catchError((errMsg) {
displayToast("Error: " + errMsg.toString(), context);
}))
.user;
if (firebaseUser != null) {
setState(() {
loading = true;
});
usersRef.child(firebaseUser.uid).once().then((DataSnapshot snap) {
if (snap.value != null) {
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (context) {
return LocationHome();
}));
displayToast("Succesfully LoggedIn!", context);
} else {
firebaseAuth.signOut();
displayToast("No user found! Please try SignUp", context);
}
});
} else {
displayToast("Error Occured! Cannot log you in", context);
}
}
}
}
And for Registration the code is below:
Future validateForm() async {
FormState formSate = _formKey.currentState;
if (formSate.validate()) {
final User firebaseUser = (await firebaseAuth
.createUserWithEmailAndPassword(
email: _emailcontroller.text,
password: _passwordcontroller.text)
.catchError((errMsg) {
displayToast("Error: " + errMsg.toString(), context);
}))
.user;
if (firebaseUser != null) {
Map userDataMap = {
"name": _namecontroller.text.trim(),
"email": _emailcontroller.text.trim(),
"phone": _phonecontroller.text.trim(),
};
usersRef.child(firebaseUser.uid).set(userDataMap);
displayToast("Succesfully Registered!", context);
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (context) {
return LocationHome();
}));
} else {
displayToast("User was unable to create", context);
}
}
}
}
The main.dart file is also coded correctly:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
SharedPreferences preferences = await SharedPreferences.getInstance();
var circle = preferences.getString("circle");
runApp(MaterialApp(
title: 'TaakStore',
home: circle == null ? Login() : Home(),
));
}
DatabaseReference usersRef =
FirebaseDatabase.instance.reference().child("users");
Dont worry about the displayToast function. It is a function manually created with flutter toast.
To display a toast, try the following:
try {
await FirebaseAuth.instance.signInWithEmailAndPassword(
email: _emailcontroller.text,
password: _passwordcontroller.text
);
} on FirebaseAuthException catch (e) {
displayToast("Error: " + e.message.toString(), context);
print(e.message);
}
To check if the user is logged in or not use the following:
FirebaseAuth.instance
.authStateChanges()
.listen((User user) {
if (user == null) {
print('User is currently signed out!');
} else {
print('User is signed in!');
}
});
authStateChanges() is of type Stream<User> which will listen for any changes on the state of a user. So if user is logged in, it will return a valid user object and you can navigate to the home screen. Therefore no need to use shared preferences.
To display a toast
try {
await FirebaseAuth.instance.signInWithEmailAndPassword(
email: _emailcontroller.text,
password: _passwordcontroller.text
);
} on FirebaseAuthException catch (e) {
displayToast("Error: " + e.message.toString(), context);
print(e.message);
}
To check if the user is logged in
//inside the main.dart in the "MaterialApp" widget
MaterialApp(home:buildHome(),)
buildHome(){return StreamBuilder(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (BuildContext context, AsyncSnapshot<User> snapshot) {
if (snapshot.hasData) {
print(snapshot);
//if the user is logged in return what you want
return "";
} else {
//else return what you want also
return"";
}
},
);}

FirebaseAuth.instance is null

Hey I'm new to flutter development and I got this message when trying to do SignUp to Firebase
I/flutter (23432): NoSuchMethodError: The method 'createUserWithEmailAndPassword' was called on null.
I/flutter (23432): Receiver: null
And here is the code
AuthServide.dart
class AuthServices {
static FirebaseAuth _auth = FirebaseAuth.instance;
static Future<SignInSignUpResult> signUp(String email, String password,
String name, List<String> selectedGenres, String selectedLanguage) async {
try {
log(_auth.toString());
log("Proses Sign Up");
UserCredential result = await _auth.createUserWithEmailAndPassword(
email: email, password: password);
log("Selesai Sign Up");
log(result.toString());
Users user = result.user.convertToUser(
name: name,
selectedGenres: selectedGenres,
selectedLanguage: selectedLanguage);
await UserServices.updateUser(user);
return SignInSignUpResult(user: user);
} catch (e) {
return SignInSignUpResult(message: e.toString());
}
}
}
After I check the log I know that FirebaseAuth.instance is null, but why?
I'm currently following a tutorial on an online flutter class and I got stuck here.
Anybody can help me?
Thanks
You can try this. I think it will work for you. Make sure you added the firebase auth pakage firebase_auth: ^0.18.4+1
import 'package:firebase_auth/firebase_auth.dart';
class AuthMethods {
// CONDITION ? TRUE: FLASE
User _userFromFirebaseUser(FirebaseUser user) {
return user != null ? User(userId: user.uid) : null;
}
Future signInWithEmailAndPassword(String email, String password) async {
try {
AuthResult result = await _auth.signInWithEmailAndPassword(
email: email, password: password);
FirebaseUser firebaseUser = result.user;
return _userFromFirebaseUser(firebaseUser);
} catch (e) {
print(e.toString());
}
}
try {
AuthResult result = await _auth.createUserWithEmailAndPassword(
email: email, password: password);
FirebaseUser firebaseUser = result.user;
return _userFromFirebaseUser(firebaseUser);
} catch (e) {
print(e.toString());
}
}
Future resetPass(String email) async {
try {
return await _auth.sendPasswordResetEmail(email: email);
} catch (e) {
print(e.toString());
}
}
Future signOut() async {
try {
return await _auth.signOut();
} catch (e) {}
}
}

How to check whether the user email id exists?

I am using the below code for google sign in flutter app with firebase, which is working successfully.
How should I check that whether the email id used is already existing in the firebase authentication?
This I need to ensure that I update the user information in the firestore database accordingly.
Future<User> _handleSignIn() async {
User user;
bool userSignedIn = await _googleSignIn.isSignedIn();
setState(() {
isUserSignedIn = userSignedIn;
});
if (isUserSignedIn) {
user = _auth.currentUser;
}
else {
final GoogleSignInAccount googleUser = await _googleSignIn.signIn();
final GoogleSignInAuthentication googleAuth = await googleUser.authentication;
final AuthCredential credential = GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
user = (await _auth.signInWithCredential(credential)).user;
userSignedIn = await _googleSignIn.isSignedIn();
setState(() {
isUserSignedIn = userSignedIn;
});
}
return user;
}
Please guide me for this
There is a Generic exception related to Firebase Authentication. which is the class FirebaseAuthException. It comes with codes related to errors including email already exists so you would write something like:
try {
//your signIn method here
//i assumed handleSignIn
handleSignIn();
} on FirebaseAuthException catch (e) {
//exception that occurs if e-mail already exists
if (e.code == 'email-already-in-use') {
// the rest of your code here
}
}
Then this is the method you should use I have used to solve my problem
this is my auth.dart file
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:gfd_official/User/User.dart';
import 'package:gfd_official/services/database.dart';
import 'package:google_sign_in/google_sign_in.dart';
String photoUrl;
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
// create user obj based on firebase user
Userdat _userFromFirebaseUser(User user) {
return user != null ? Userdat(uid: user.uid) : null;
}
// auth change user stream
Stream<Userdat> get user {
return _auth
.authStateChanges()
//.map((FirebaseUser user) => _userFromFirebaseUser(user));
.map(_userFromFirebaseUser);
}
Future signInWithGoogle() async {
GoogleSignIn googleSignIn = GoogleSignIn();
final acc = await googleSignIn.signIn();
final auth = await acc.authentication;
final credential = GoogleAuthProvider.credential(
accessToken: auth.accessToken, idToken: auth.idToken);
try {
final res = await _auth.signInWithCredential(credential);
User user = res.user;
photoUrl = user.photoURL;
UserHelper.saveUser(user);
return _userFromFirebaseUser(user);
} catch (e) {
print(e.toString());
return null;
}
}
Future logOut() {
try {
GoogleSignIn().signOut();
return _auth.signOut();
} catch (e) {
print(e.toString());
return null;
}
}
}
class UserHelper {
static FirebaseFirestore _db = FirebaseFirestore.instance;
static saveUser(User user) async {
Map<String, dynamic> userData = {
"name": user.displayName,
"email": user.email,
"role": "basic",
};
try {
final userRef = _db.collection("users").doc(user.uid);
if ((await userRef.get()).exists) {
await userRef.update({});
} else {
await _db
.collection("users")
.doc(user.uid)
.set(userData, SetOptions(merge: true));
}
} catch (e) {
print(e.toString());
}
}
}
In this the save user function will create database for new user and if user already exist then it will let it remain same as it is.
I am creating a document for every user with his/her uid.

Check if user exists on FIrestore

I have this function that use SignInWithGogle to Sign In/Up an user
void _signInWithGoogle() async {
final GoogleSignInAccount googleUser = await _googleSignIn.signIn();
final GoogleSignInAuthentication googleAuth =
await googleUser.authentication;
final AuthCredential credential = GoogleAuthProvider.getCredential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
final FirebaseUser user = (await _auth.signInWithCredential(credential)).user;
assert(user.email != null);
assert(user.displayName != null);
assert(!user.isAnonymous);
assert(await user.getIdToken() != null);
final FirebaseUser currentUser = await _auth.currentUser();
assert(user.uid == currentUser.uid);
print('user email here $user.email');
setState(() {
if (user != null) {
_success = true;
_userID = user.uid;
final userToSubmit = User(
email: user.email,
id: user.uid,
name: user.displayName,
owner: false,
favorites: null);
print('USER ID USER ID USER ID ${user.uid}');
DocumentReference dbRef =
Firestore.instance.collection('users').document(user.uid);
dbRef.get().then((value) {
print('VALUE EXISTS? ${value.exists} y VALUE $value');
value.exists ?
_goToHomeScreen(user)
:
dbRef
.collection('prf')
.document('profile')
.setData(userToSubmit.toJson())
.then(_goToHomeScreen(user));
});
} else {
_success = false;
}
});
}
The problem is that when the user logOut and then login it sets the new Data and the user lost everything they have like favorites and so on... I thought that checking if the document exists should resolve the problem, but despite the document is there exists returns false.
This is the piece of code I'm talking about
DocumentReference dbRef =
Firestore.instance.collection('users').document(user.uid);
dbRef.get().then((value) {
print('VALUE EXISTS? ${value.exists} y VALUE $value');
value.exists ?
_goToHomeScreen(user)
:
dbRef
.collection('prf')
.document('profile')
.setData(userToSubmit.toJson())
.then(_goToHomeScreen(user));
});
}
Why it returns false even if the user.uid and the document exists? What am I doing wrong?
The final solution I implemented is this:
FirebaseUser firebaseUser;
try {
firebaseUser = (await FirebaseAuth.instance.signInWithCredential(credential)).user;
} catch(e) {
print(e.toString());
} finally {
final userToSubmit = User(
email: firebaseUser.email,
id: firebaseUser.uid,
name: firebaseUser.displayName,
owner: false,
restauranteAdded: false);
DocumentReference dbRef =
Firestore.instance.collection('users').document(firebaseUser.uid).collection('nameOfTheCollection').document('documentName');
dbRef.get().then((data) {
data.exists ? _goToHomeScreen(user) :
_createUser();
});
This way I check if user document exists in my Firestore Database and if not it creates it.
There is a lot going on. In general, you're not updating any state object with the setState() method. setState is used to tell a widget that some state has changed--and it's called synchronously with the change, but here where you're just checking to see if a document exists and nothing is changing, it's not needed.
Also it doesn't appear that you're actually returning a user object at all, so this code as I read it will always return false.
You should remove setState here altogether (not needed for what you're doing)
Run a sign in method that returns the user object you want do do things with.
Access your db once you know you have a signed in user (i.e. w/ uid of signed in user)
Here is an option to try:
//Create a User class so that you don't have to call FireBase in your methods.
class User {
User({
#required this.uid,
this.email,
this.displayName,
});
final String uid;
final String email;//these are whatever you want
final String displayName;
}
//put sign in methods into an abstract class. Don't have to, but keeps code flexible if you don't stay with firebase
abstract class AuthBase {
//add in other signin methods as needed
Future<User> signInWithGoogle();
}
//Implement AuthBase in class Auth
class Auth implements AuthBase {
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
//Get a User from Firebase User
User _userFromFirebase(FirebaseUser user) {
if (user == null) {
return null;
}
return User(
uid: user.uid,
displayName: user.displayName,
email: user.email,
);
}
//your signinWithGoogle method
Future<User> signInWithGoogle() async {
//where User is the class you've created
GoogleSignIn googleSignIn = GoogleSignIn();
GoogleSignInAccount googleUser = await googleSignIn.signIn();
if (googleUser != null) {
GoogleSignInAuthentication googleAuth = await googleUser.authentication;
if (googleAuth.idToken != null && googleAuth.accessToken != null) {
final authResult = await _firebaseAuth.signInWithCredential( //await your instance of FirebaseAuth
GoogleAuthProvider.getCredential(
idToken: googleAuth.idToken,
accessToken: googleAuth.accessToken,
),
);
return _userFromFirebase(authResult.user); //your signed in firebase user that you can do stuff with uid, email, displayname, etc.
} else {
throw PlatformException(
code: 'ERROR_MISSING_GOOGLE_AUTH_TOKEN',
message: '',
);
}
} else {
throw PlatformException(
code: 'ERROR_ABORTED_BY_USER',
message: '',
);
}
}

How to detect an async operation is successful or not in Flutter

In my app, after authentication, user can move to the next screen.
signUpWithEmail().then((user) {
Navigator.push(context,
MaterialPageRoute(builder: (context) {
return HomePage();
}));
}).catchError((error) {
print("THE ERROR : $error");
});
Now signUpWithEmail may fail for various reasons like : invalid e-mail, internet connectivity failure and so on. How can I detect those errors and prevent navigation? Here is signUpWithEmail() method:
Future<FirebaseUser> signUpWithEmail() async {
String email = emailControlller.text;
String password = passwordControlller.text;
FirebaseUser user = await FirebaseAuth.instance
.createUserWithEmailAndPassword(
email: emailControlller.text,
password: passwordControlller.text,
)
.then((user) {
// set balance to 0
Firestore.instance
.collection("users")
.document(user.uid)
.setData({"cash": 0});
}).catchError((e) => print("error : $e"));
return user;
}
You is returning to signUpWithEmail() of anyway, you don't throw the error, so it never will enter on
.catchError((error) {
print("THE ERROR : $error");
})
To fix it you must throw the error on your signUpWithEmail(). Try something like it.
Future<FirebaseUser> signUpWithEmail() async {
String email = emailControlller.text;
String password = passwordControlller.text;
FirebaseUser user = await FirebaseAuth.instance
.createUserWithEmailAndPassword(
email: emailControlller.text,
password: passwordControlller.text,
)
.then((user) {
// set balance to 0
Firestore.instance
.collection("users")
.document(user.uid)
.setData({"cash": 0});
}).catchError((e) => {
print("error : $e")
throw("Your error") // It return to catch
});
return user;
}
Let me know if you can make it.
Use FutureBuilder widget and wrap the logic in the builder method

Resources