onPressed takes 2 clicks to complete nested functions - flutter

When a user enters their information in an overlay, presses submit, 4 things should happen.
User's account is registered in firestore
User has a document created with their info
the overlay is closed
nav() page is called
On the first click, [1] happens and then stops at the collection reference.
On the second click, [1] fails, but [2-4] successfully go through. I think it has to do with when the user info not coming back from the registry fast enough but the prints are coming back correctly so I'm not sure.
onPressed function in overlay
onPressed: () async{
await register(_email, _password);
await signUp(_email, firstName, lastName, userName);
hideNewUserOverlay();
var navTab = 2;
Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (context) => const Nav(),
settings: RouteSettings(arguments: navTab)
));
}
Register and Sign Up
Future<void> register(String email, String password) async {
try {
// ignore: unused_local_variable
await auth.createUserWithEmailAndPassword(
email: email,
password: password); // <-- user account is created on first press
} catch (e) {
debugPrint('>> register: create new user error');
}
user = auth.currentUser!;
debugPrint(' register: current user got');
String userID = user.uid;
debugPrint(' register: user.id = $userID');
}
Future<void> signUp(
String email, String firstName, String lastName, String userName) async {
user = auth.currentUser!;
debugPrint('>> signUp: current user got');
String userID = user.uid;
debugPrint(' signUp: user.id = $userID'); // all debugs print out correctly
try {
debugPrint(' signUp: document creation code Start');
await collectionReference.doc(userID).set({ // first click stops here, takes 2 clicks to finish
'profileImageUrl': 'https://firebasestorage.googleapis.com/v0/b/commentaries-d82cd.appspot.com/o/defaultProfileImage.png?alt=media&token=07e4d649-3da7-4f9f-8cf9-062ac9cc9507',
'userID': userID,
'accountCreated': DateTime.now(),
'email': email,
'userName': userName,
'firstName': firstName,
'lastName': lastName,
});
debugPrint(' signUp: User Document Created');
} catch (e) {
debugPrint(' signUp: create new user error');
}
}

I think it doesn't work all at once because you are making asynchronous calls but your onPressed function is not async
Try this:
onPressed: () async{
await register(_email, _password);
await signUp(_email, firstName, lastName, userName);
hideNewUserOverlay();
var navTab = 2;
Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (context) => const Nav(),
settings: RouteSettings(arguments: navTab)
));
}

You can add callback to your functions, so you know they are done.
See:
Future<void> register(String email, String password, VoidCallback callback) async {
try {
// ignore: unused_local_variable
await auth.createUserWithEmailAndPassword(
email: email,
password: password); // <-- user account is created on first press
} catch (e) {
debugPrint('>> register: create new user error');
}
user = auth.currentUser!;
debugPrint(' register: current user got');
String userID = user.uid;
debugPrint('register: user.id = $userID');
callback(); //here you will send the flag informing the job is done.
}
Now let´s do the same with the other function.
Future<void> signUp(
String email, String firstName, String lastName, String userName, VoidCallback callback) async {
user = auth.currentUser!;
debugPrint('>> signUp: current user got');
String userID = user.uid;
debugPrint(' signUp: user.id = $userID'); // all debugs print out correctly
try {
debugPrint(' signUp: document creation code Start');
await collectionReference.doc(userID).set({ // first click stops here, takes 2 clicks to finish
'profileImageUrl': 'https://firebasestorage.googleapis.com/v0/b/commentaries-d82cd.appspot.com/o/defaultProfileImage.png?alt=media&token=07e4d649-3da7-4f9f-8cf9-062ac9cc9507',
'userID': userID,
'accountCreated': DateTime.now(),
'email': email,
'userName': userName,
'firstName': firstName,
'lastName': lastName,
});
debugPrint(' signUp: User Document Created');
callback();
} catch (e) {
debugPrint(' signUp: create new user error');
}
}
now you can make this with steps:
onPressed: () async{
await register(_email, _password, (){
await signUp(_email, firstName, lastName, userName, (){
hideNewUserOverlay();
var navTab = 2;
Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (context) => const Nav(),
settings: RouteSettings(arguments: navTab)
));
});
});
}
Explaination: What I did was to place the next step inside the callback. So, you know everything will continue only when you really want it to.
next things to do to make your code better:
Named paramters to help code reading.
Create functions to callbacks, making the code more readable too and maintenable.
remove the awaits no more needed.
Create a Loading while the process go, as this can take long.
Create a block to not allow multiples clicks in this button.
Create callbacks to handle failures.

Related

Can we set user displayName in firebase using password authentication through flutter? [duplicate]

I can save the email and the password with Firebase Authentication. I also save this information with Cloud Firestore. But how can I add and save the displayName after registering?
my code:
Future registerWithEmailAndPassword(String email, String password) async {
try {
AuthResult result = await _auth.createUserWithEmailAndPassword(
email: email, password: password);
FirebaseUser user = result.user;
// create a new document for the user with the uid
await DatabaseService(uid: user.uid)
.updateUserData('User', user.email, 'test.de/test.png', user.uid);
return _userFromFirebaseUser(user);
} catch (e) {
print(e.toString());
return null;
}
}
the register form button:
onPressed: () async {
if (_formKey.currentState.validate()) {
setState(() => loading = true);
dynamic result = await _auth
.registerWithEmailAndPassword(
email, password);
if (result == null) {
setState(() {
error = 'Valid email, please';
loading = false;
});
}
}
}
You can use updateProfile method given in the FirebaseUser class to update the name and photo url.
updateProfile({String displayName, String photoURL}).
For email you can use the different method
updateEmail(String newEmail) which is a async method.
Or to save the username directly into the firestore, after successful login you can use 'set' method of the FirebaseFirestore to save email, password and the username.
Here's the code if you want to save the Username together with the email and password
final FirebaseAuth _auth = FirebaseAuth.instance;
//register with email & password & save username instantly
Future registerWithEmailAndPassword(String name, String password, String email) async {
try {
UserCredential result = await _auth.createUserWithEmailAndPassword(email: email, password: password);
User user = result.user;
user.updateProfile(displayName: name); //added this line
return _user(user);
} catch(e) {
print(e.toString());
return null;
}
}
late User userFirebase;
Future<UserCredential> userCredential =await //Your code signin or signup
await userCredential.then((UserCredential value) {
userFirebase = value.user!;
});
await userFirebase.updateDisplayName(event.name);
You can use updateDisplayName() and updatePhotoURL() methods on a User object to add the displayName/profile image to Firebase user.
final userCredential = await _auth.createUserWithEmailAndPassword(
email: email,
password: password,
);
//After creating a user in Firebase, we then are able to change name/pictue
await userCredential.user?.updateDisplayName(name);
await userCredential.user?.updatePhotoURL(imageUrl);
As of october 2021, updateProfile() is deprecated, and you should use updateDisplayName() and updatePhotoURL() instead.

Cloud Firestore Database is not displaying users after authentication?

I am trying to do authentication using createUserWithEmailAndPassword .The user iis getting created since I can signin using the email and password which created using createUserWithEmailAndPassword .But my firestore database is not showing the document which I have created using createUserWithEmailAndPassword().
This is my code:
onPressed: () async {
try {
final newuser = await FirebaseAuth.instance
.createUserWithEmailAndPassword(
email: email ?? 'error',
password: password ?? 'error',
);
if (newuser != null) {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => home()),
);
}
} catch (e) {
print(e);
}
I have created 3 users with this method and I'm able to login with these credential but these users are not showing in my Firestore Database.
As #ashish already mentioned, you need to store all your extra parameters in the firestore as documents. A better way to implement this is to use different function to register user and create a user document related to that registered user.
Below is an example I created to help out!
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
void main() {
String email = "test#test.com";
String password = "123456";
String firstName = "test";
String lastName = "test";
String phoneNumber = "09876543456";
/// This is the function to create new user
Future<void> createUser(
email,
password,
firstName,
lastName,
phoneNumber,
/*... and any other params you wish to collect */
) async {
// firebase auth
final FirebaseAuth auth = FirebaseAuth.instance;
// firestore db
FirebaseFirestore db = FirebaseFirestore.instance;
// try-catch creation process to accurate exception
try {
final credential = await auth.createUserWithEmailAndPassword(
email: email,
password: password,
);
// get user id
String userID = credential.user!.uid;
// creating user profile on db
await db.collection("users").doc(userID).set({
"id": userID,
"firstName": firstName,
"lastName": lastName,
"phoneNumber": phoneNumber,
// ... other params
});
print("Signed up successfully!");
} on FirebaseAuthException catch (error) {
print("Something is wrong: $error");
}
}
/// this is the create button
TextButton(
onPressed: () async {
await createUser(
email,
password,
firstName,
lastName,
phoneNumber,
/*... and any other params you wish to collect */
);
},
child: const Text("Create test user"),
);
}
Let me know if you need anything else :). BYE!!!
To store the data in the database you need to use a Firestore instance to store data in firebase Firestore.
onPressed: () async {
try {
final newuser = await FirebaseAuth.instance
.createUserWithEmailAndPassword(
email: email ?? 'error',
password: password ?? 'error',
);
await FirebaseFirestore.instance.collection('Users').doc(newuser!.uid).set({
email: newuser.email
});
if (newuser != null) {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => home()),
);
}
} catch (e) {
print(e);
}
}
You need to put the data which you want to store in firestore in set() function.
Firebase Authentication data are show into the login into firebase then on tap of Authentication options menu -> user section , you can view store 3 users data in this section

updateDisplayName() not working after running createUserWithEmailAndPassword

I have a future method in a AuthService class to create new users with the inbuilt function createUserWithEmailAndPassword, but right after that I am trying to update the display name of that User? user but this never updates.
Future regEmailPass(String email, String password, String? username) async {
try {
UserCredential result = await _auth.createUserWithEmailAndPassword(
email: email, password: password);
User? user = result.user;
await user?.updateDisplayName(username);
UserInApp? userInApp = _userFromFireBaseUser(user);
await DatabaseService(user: userInApp).firstTimeCreateDB(username!);
return userInApp;
} catch (e) {
print(e.toString());
return null;
}
}
I even tried using the await user.reload() or double checking if my user was null in the first place, but in every situation, the displayName was set to null in the firestore database.

Create an user in Firebase without automatically login

I have wrote a code, which should avoid the automatically login when an user get created.
The code worked until I used the dart migration tool. Now when I use this code with null-safety, then the secondaryApp is not created and another user get logged in.
Can someone please explain me, what I am doing wrong? I need to fix this code as fast as possible, but I just can't find the mistake.
I really appreciate any help.
code login:
InkWell(
onTap: () async {
await authProvider.signUpUser(
usernameController.text.trim(),
emailController.text.trim(),
passwordController.text.trim(),
firstNameController.text.trim(),
lastNameController.text.trim(),
_birthDateInString,
_genderSelected);
})
code (database request):
Future<bool> signUpUser(String username, String email, String password,
String firstName, String lastName, String? birthday, String? gender) async {
try {
_status = Status.Authenticating;
notifyListeners(); //changing status
FirebaseApp secondaryApp = await Firebase.initializeApp(
name: 'Secondary',
options: Firebase.app().options,
);
try {
UserCredential? credential = await FirebaseAuth.instanceFor(
app: secondaryApp)
.createUserWithEmailAndPassword(email: email, password: password)
.then((result) async {
//User user = result.user;
_userServices.createUser(
uid: result.user!.uid,
username: username,
email: email,
firstName: firstName,
lastName: lastName,
birthday: birthday,
gender: gender,
status: 'aktiv',
role: 'User',
);
});
print('user created');
await credential?.user!.sendEmailVerification();
} on FirebaseAuthException catch (e) {
print(e);
}
print('secondapp deleted');
await secondaryApp.delete();
_status = Status.Unauthenticated;
notifyListeners();
print('secondapp deleted');
return true;
} catch (e) {
_status = Status.Unauthenticated;
notifyListeners();
print(e.toString());
return false;
}
}
A little difficult to completely debug your project without a little more context but I tried to clean up just a bit and add a few print statements to help you out, hope this helps :)
Future<bool> signUpUser(
String username,
String email,
String password,
String firstName,
String lastName,
String? birthday,
String? gender,
) async {
try {
_status = Status.Authenticating;
notifyListeners(); //changing status
FirebaseApp secondaryApp = await Firebase.initializeApp(
name: 'Secondary',
options: Firebase.app().options,
);
/// here is our user:)
var _user;
try {
UserCredential? credential = await FirebaseAuth.instanceFor(app: secondaryApp)
.createUserWithEmailAndPassword(email: email, password: password);
print('we got our credentials lets take a look to see if we have them ${credential}');
var uid = credential.user?.uid;
if (uid != null) {
_userServices.createUser(
uid: uid,
username: username,
email: email,
firstName: firstName,
lastName: lastName,
birthday: birthday,
gender: gender,
status: 'aktiv',
role: 'User',
);
print('user created');
}
_user = credential?.user;
if (_user != null) {
await credential?.user.sendEmailVerification();
print('done with our send email verification!');
}
} on FirebaseAuthException catch (e) {
print(e);
}
if (_user != null) {
/// this means that we successfully authenticated our user consider returning from the function right here!!
}
print('secondapp deleted');
await secondaryApp.delete();
_status = Status.Unauthenticated;
notifyListeners();
print('secondapp deleted');
return true;
} catch (e) {
_status = Status.Unauthenticated;
notifyListeners();
print(e.toString());
return false;
}
}
One other thing that I noticed with your code is that if a User successfully authenticates then you automatically delete your secondaryApp. Is this your desired behavior?
I went ahead and added a note for you to add some code in there if you want but didn't want to dramatically change your code.

How can I resolve a problem with firebase registration?

I'm writing an application that have to register a user with firebase and than do the login. I wrote a function for the registration but the output give me this error:
PlatformException(error, Given String is empty or null, null)
This is the function:
Future registerWithEmailAndPassword(String email, String password) async {
try {
AuthResult result = await FirebaseAuth.instance.createUserWithEmailAndPassword(email: email, password: password);
FirebaseUser user = result.user;
Navigator.push(context, MaterialPageRoute(builder: (context) => Login()));
print(user);
return user;
} catch (e) {
print(e.toString());
return null;
}
}