Flutter : How to implement a callback - firebase

How to implement a callback return the error message?
Login function from AuthService class:
static void login(String email, String password) async {
try {
await FirebaseAuth.instance.signInWithEmailAndPassword(email: email, password: password);
} catch (e) {
print(e);
}
}
Submit function from Login class:
_submit() {
// If fail to login then return the error message
AuthService.login(_email, _password);
}

Try the following:
Future<AuthResult> login(String email, String password) async {
try {
Future<AuthResult> result = await FirebaseAuth.instance.signInWithEmailAndPassword(email: email, password: password);
return result;
} catch (e) {
print(e);
}
}
Then you can call the method like this:
_submit() {
// If fail to login then return the error message
login(_email, _password).then((result) => {
print(result);
});
}
The method signInWithEmailAndPassword returns Future<AuthResult>, therefore assign it to that type, the await keyword will wait until the method finishes execution and then it will return the value of type Future<AuthResult>.
A future represents the result of an asynchronous operation, and can have two states: uncompleted or completed.
When you call the method login(), you can add the then() method which registers callbacks to be called when this future completes.
When this future completes with a value, the onValue callback will be called with that value.
https://api.dartlang.org/stable/2.7.0/dart-async/Future/then.html

// wrapping the firebase calls
Future<FirebaseUser> loginUser({String email, String password}) {
return FirebaseAuth.instance
.signInWithEmailAndPassword(email: email, password: password);
}
the user info is returned, you not sure what you need the callback for?
userInfo = await AuthService().loginUser(email: _email, password: _password);

Related

Is there a way to use the results of a catch block inside a widget in Flutter

I am building a flutter app with Firebase as the back end.
I have created an AuthService class on a separate file and import and use the Auth functions inside the log in screen.
This is my AuthService Class.
class AuthService {
Future<UserModel?> signInWithEmailAndPassword(
String email, String password) async {
try {
final cred = await _auth.signInWithEmailAndPassword(
email: email, password: password);
return _userFromFirebase(cred.user);
} on auth.FirebaseAuthException catch (e) {
print(e.toString());
return null;
}
}
}
In the sign in page, I initialize the function:
final auth = Provider.of<AuthService>(context);
Then use it in an onPressed :
press: () async {
// SIGN IN WITH EMAIL AND PASSWORD
dynamic result =
await auth.signInWithEmailAndPassword(
email, password);
// IF SIGN IN FAILS
if (result == null) {
setState(() {
errorSigningIn = 'Sign in error';
//this is where I want to use the error response.
});
}
},
I am stuck on using the error I catch in the signInWithEmailAndPassword function and assigning it to the errorSigningIn variable in the SignIn widget.
I am new to this, please help.
Thanks.
You can create your own class to handle auth results. For example:
class AuthResult {
final int code;
final UserModel? user;
final String? errorMessage;
AuthResult(this.code, {
this.user,
this.errorMessage,
});
}
This class can help you to handle all cases of logging in. And this is what you should do with your signing in method:
class AuthService {
Future<AuthResult> signInWithEmailAndPassword(
String email, String password) async {
try {
final cred = await _auth.signInWithEmailAndPassword(
email: email, password: password);
return AuthResult(200, user: _userFromFirebase(cred.user));
} on auth.FirebaseAuthException catch (e) {
print(e.toString());
return AuthResult(0 /*<-- your error result code*/, e.toString());
}
}
}
And, finally, your onPressed:
press: () async {
// SIGN IN WITH EMAIL AND PASSWORD
AuthResult result =
await auth.signInWithEmailAndPassword(
email, password);
// IF SIGN IN FAILS
if (result.code != 200) {
setState(() {
errorSigningIn = result.errorMessage; //<-- Get your error message
//this is where I want to use the error response.
});
}
},

Flutter - how to trigger a function after the async function is completed

Hi I would like for the addUserToFirestore() function to trigger after the code above has done being executed
Future<void> registerAccount(
String email,
String displayName,
String password,
void Function(FirebaseAuthException e) errorCallback) async {
try {
var credential = await FirebaseAuth.instance
.createUserWithEmailAndPassword(email: email, password: password);
await credential.user!.updateDisplayName(displayName);
addUserToFirestore();
} on FirebaseAuthException catch (e) {
errorCallback(e);
}
}
try and change your code to:
await credential.user!.updateDisplayName(displayName)
.then((_) => addUserToFirestore());
so that addUserToFirestore() will only fire after updateDisplayName() is complete

The getter 'uid' not defined

i'm trying to create a food track app on android studio, it's my first time and i'm working with firebase_auth 3.3.12. my code in the aut.dart is:
import 'package:firebase_auth/firebase_auth.dart';
import 'package:my_firstapp/models/user_model.dart';
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
AuthService();
// create user object based on FirebaseUser.
UserModel _userFromUser(User) {
return user != null ? UserModel(uid: user.uid) : null;
}
// auth change user stream
Stream<UserModel> get user {
return _auth.authStateChanges()
.map(_userFromUser);
}
Future<UserModel> getUser() async {
User user = await _auth.currentUser();
return _userFromUser(user);
}
// sign in with email and password
Future signInWithEmailAndPassword(String email, String password) async {
try {
UserCredential result = await _auth.signInWithEmailAndPassword(email: email, password: password);
User user = result.user;
return _userFromUser(user);
} catch(e) {
print(e.toString());
return null;
}
}
// sign up with email and password
Future registerWithEmailAndPassword(String email, String password) async {
try {
UserCredential result = await _auth.createUserWithEmailAndPassword(email: email, password: password);
User user = result.user;
// create a new user document in database
return _userFromUser(user);
} catch(e) {
print(e.toString());
return null;
}
}
// sign out
Future signOut() async {
try {
return await _auth.signOut();
} catch(e){
print(e.toString());
return null;
}
}
}
However i'm getting 2 errors:
-The getter 'uid' isn't defined for the type 'Stream';
-The expression "await _auth.currentUser()" doesn't evaluate to a function, so it can't be invoked.
How can i rewrite the code? thanks
The _auth.currentUser is not a function (it used to be, but changed about a year ago), but rather a property. It also isn't asynchronous, so you don't need await nor to return a Future.
So:
UserModel getUser() {
User user = _auth.currentUser;
return _userFromUser(user);
}
In this code, your argument is capitalised ('User') but in the code block you write 'user'.
UserModel _userFromUser(User) {
return user != null ? UserModel(uid: user.uid) : null;
}
Furthermore, for _auth.currentUser(), you do not need to use await as it does not return a future.

Future<UserData> returns a Future<UserData> instead of a UserData variable

I have a widget that uses a Future - async that returns a UserData variable, but for some reason, I'm getting a Future variable instead.
a line of code from a widget Build:
dynamic user = Auth.signIn(_email, _password);
Auth Class:
Future<UserData> signIn(String email, String password) async {
try{
UserCredential userCredential = await _auth.signInWithEmailAndPassword(
email: email,
password: password
);
print('signed in!');
return _createUserFromFirebase(userCredential.user.uid, null);
} on FirebaseAuthException catch (e) {
if (e.code == 'user-not-found') {
print('No user found for that email.');
} else if (e.code == 'wrong-password') {
print('Wrong password provided for that user.');
}
return null;
}
}
You could try something like
return Future.value(_createUserFromFirebase(userCredential.user.uid, null);
instead of
return _createUserFromFirebase(userCredential.user.uid, null);
Async cannot be used without it being in a function with await. You can't randomly retrieve an asynchronous variable randomly in the script without loading it asynchronously, because the future might not have the data ready to be used.
If you need the future variable's future value to be in a widget try using the FutureBuilder widget. This will make it so that the user data is loaded asynchronously and a place holder of your choice can be shown before it loads. This would allow you to use the UserData type in your widget.
Or you can get the user by having the button have an async variable that sets a variable in the widget from null to the UserData after signing in.
I've added a new future method inside the widget class that saves the user in a variable:
Future<void> getUserData(String email, String password) async{
_user = await _auth.signIn(email, password);
print('user: $_user');
}

Firebase + Flutter: can't lock access to unverified email accounts

I'd like to block out people who didn't verify their email so i figured out this code for sign up:
// sign up
Future signUp(String email, String password) async {
try {
await _auth.createUserWithEmailAndPassword(
email: email, password: password);
} catch (e) {
print('An error has occured by creating a new user');
print(
e.toString(),
);
}
try {
final FirebaseUser _user = await _auth.currentUser();
await _user.sendEmailVerification();
} catch (error) {
print("An error occured while trying to send email verification");
print(error.toString());
}
try {
await _auth.signOut();
} catch (err) {
print(err);
}
}
and this for sign in:
//Sign In with Email and Pass
Future signInWithEmailAndPassword(String email, String password) async {
FirebaseUser _user = await FirebaseAuth.instance.currentUser();
if (_user != null && _user.isEmailVerified == true) {
try {
await _auth.signInWithEmailAndPassword(
email: email, password: password);
return _user;
} catch (e) {
return null;
}
} else {
return null;
}
}
_auth is just an instance of FirebaseAuth.
The problem is that i can login even if i didnt verify the email.
Firebase Auth doesn't stop accounts from signing in if the user hasn't verified their email address yet. You can check that property _user.isEmailVerified to find out the state of that validation after the user signs in, and you can determine from there what the user should see.
isEmailVerified can be a little bit of trouble to get working correctly.
Make sure you are calling
await FirebaseAuth.instance.currentUser()..reload();
before your are calling isEmailVerified also in my own experience and I don't know if this is just something I was doing wrong but this did not work from my Auth class this did not start working until I put the code directly in initState() of my widget that checks whether the user is verified. Like I said that part might have been something I did wrong. Like stated this will not listen for change you must check yourself either periodically or at a point that you know email is verified.
Future(() async {
_timer = Timer.periodic(Duration(seconds: 10), (timer) async {
await FirebaseAuth.instance.currentUser()
..reload();
var user = await FirebaseAuth.instance.currentUser();
if (user.isEmailVerified) {
timer.cancel();
Navigator.of(context).popAndPushNamed(HearingsScreen.routeName);
}
});
});
So it checks every 10 seconds to see if the user has verified their email not the most elegant solution. The page I have this on just displays a message 'Please verify your email' so its not like this is interrupting other code. If your app is performing other tasks this might not be an option for you. If you want to play around with isEmailVerified go ahead but i spent a week of headaches until i settled on this.

Resources