Flutter/Firebase/Firestore - Signup not pushing data to database - firebase

I am trying to record some additional user data when they sign up to my app. My signup process looks like this:
String ref = "users";
Future<bool> signUp(
String email,
String password,
String firstName,
String lastName,
) async {
try {
_status = Status.Authenticating;
notifyListeners();
await _auth
.createUserWithEmailAndPassword(email: email, password: password);
FirebaseFirestore.instance.collection(ref).doc(user.uid).set({
'id': user.uid,
'displayName': firstName.trim() + " " + lastName.trim(),
'email': email.trim(),
'createdat': DateTime.now()
});
return true;
} catch (e) {
_status = Status.Unauthenticated;
notifyListeners();
} return false;
}
I can't work out why, when the user enters their data and signs up, the specified values aren't pushed to firestore. Can someone help me out? Thanks. Here is where I have implemented my code:
child: RaisedButton(
child: Text('Create an Account', style: TextStyle(color: Colors.white),),
onPressed: () async {
if (validate()) {
_formKey.currentState.reset();
user.signUp(_emailController.text, _passwordController.text, _firstNameController.text, _lastNameController.text);
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (context) => Home()));
}
}),

So, as mentioned in the comments, it should be like this:
await FirebaseFirestore.instance.collection(ref).doc(user.uid).set({
'id': user.uid,
'displayName': firstName.trim() + " " + lastName.trim(),
'email': email.trim(),
'createdat': DateTime.now()
});

Related

The email address is badly formatted - Flutter firebase

FirebaseAuthException ([firebase_auth/invalid-email] The email address is badly formatted
when I uses flutter firebase email password auth it shows email adress badly formated. name and vehicle number is also pass to the database when authentication process is there any problem in it. why it occurs. if someone can help me to find out the problem help me
MaterialButton(
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.all(Radius.circular(20.0))),
elevation: 5.0,
height: 40,
onPressed: () {
setState(() {
showProgress = true;
});
signUp(
emailController.text,
passwordController.text,
role,
vehicleNo.text,
name.text);
},
child: Text(
"Register",
style: TextStyle(
fontSize: 20,
),
),
color: Colors.white,
)
],
),
],
),
),
),
),
),
],
),
),
);
}
void signUp(String name, String email, String password, String role,
String vehicleNo) async {
const CircularProgressIndicator();
if (_formkey.currentState!.validate()) {
await _auth
.createUserWithEmailAndPassword(
email: email.trim(), password: password.trim())
.then(
(value) => {
postDetailsToFirestore(
email,
role,
name,
vehicleNo,
),
},
)
.catchError((e) {
print("its an error");
});
}
}
postDetailsToFirestore(
String email, String role, String name, String vehicleNo) async {
FirebaseFirestore firebaseFirestore = FirebaseFirestore.instance;
User? user = _auth.currentUser;
UserModel userModel = UserModel();
userModel.email = email;
userModel.name = name;
userModel.vehicleNo = vehicleNo;
userModel.uid = user!.uid;
userModel.role = role;
await firebaseFirestore
.collection("users")
.doc(user.uid)
.set(userModel.toMap());
Navigator.pushReplacement(
context, MaterialPageRoute(builder: (context) => LoginScreen()));
}
}
When executing the SignUp function (at Material Button OnPressed), are the variables passed in the wrong order?
You're calling:
signUp(
emailController.text,
passwordController.text,
role,
vehicleNo.text,
name.text);
And signUp is defined as:
void signUp(String name, String email, String password, String role,
String vehicleNo) async {
So the order of the arguments is different between the two, leading you to call Firebase with the password value as the email address and the role value as the password.
To fix the problem, pass the arguments in the same order as signUp expects them.
It's almost always trailing whitespace, try:
postDetailsToFirestore(
email.trim(),
role,
name,
vehicleNo,
),
Alternatively you can also try to hardcode the right email address and check whether the problem is in logic or in UI.

Check if phone exists before signup/signin - Flutter Phone Authentication [duplicate]

This question already exists:
Firebase Authentication using Phone number (Error: Missing Session Info)
Closed 2 years ago.
I want to check if phone exists before signing in or signing up a user. With email registration, I used the following and I was able to tell if an email exists or not.
final url =
'https://www.googleapis.com/identitytoolkit/v3/relyingparty/$verifyPassword?key={API_KEY}';
Similarly, for phone numbers, I used the following:
final url ='https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPhoneNumber?key={API_KEY}';
final response = await http.post(
url,
body: json.encode(
{
'phoneNumber': number
},
),
);
However, I am getting the following error message:
Extracted data is {error: {code: 400, message: MISSING_SESSION_INFO, errors: [{message: MISSING_SESSION_INFO, domain: global, reason: invalid}]}}
I want to know why does it work for email but not for phone registration?
Also, is this the right way to check if a phone exists?
Here's my full code:
enum Status { Uninitialized, Authenticated, Authenticating, Unauthenticated }
class AuthProvider with ChangeNotifier {
FirebaseAuth _auth = FirebaseAuth.instance;
User _user;
Status _status = Status.Uninitialized;
TextEditingController phoneNo;
String smsOTP;
String verificationId;
String errorMessage = '';
bool logedIn = false;
bool loading = false;
Status get status => _status;
TextEditingController address = TextEditingController();
AuthProvider.initialize() {
readPrefs();
}
Future signOut() async {
_auth.signOut();
_status = Status.Unauthenticated;
notifyListeners();
return Future.delayed(Duration.zero);
}
Future<void> readPrefs() async {
await Future.delayed(Duration(seconds: 3)).then((v) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
logedIn = prefs.getBool('logedIn') ?? false;
if (!logedIn) {
print('User is not logged in');
_status = Status.Unauthenticated;
} else {
print('User is logged in');
_user = _auth.currentUser;
_status = Status.Authenticated;
}
notifyListeners();
});
}
Future<void> verifyPhone(BuildContext context, String number,String password) async {
//To be used in the verifyPhone method
final PhoneCodeSent smsOTPSent = (String verId, [int forceCodeResend]) {
this.verificationId = verId;
smsOTPDialog(context, number,password).then((value) {
_status = Status.Authenticated;
});
};
try {
await _auth.verifyPhoneNumber(
phoneNumber: number.trim(),
codeAutoRetrievalTimeout: (String verId) {
//Starts the phone number verification process for the given phone number.
//Either sends an SMS with a 6 digit code to the phone number specified, or sign's the user in and [verificationCompleted] is called.
this.verificationId = verId;
},
codeSent: smsOTPSent,
// timeout: const Duration(seconds: 20),
//If user is automatically verified (without having to type the code)
verificationCompleted: (AuthCredential credential) async {
Navigator.of(context).pop();
UserCredential result =
await _auth.signInWithCredential(credential);
User user = result.user;
if (user != null) {
//TO DO:// Here you need to save the phone and password to DB
print('Adding user to DB');
final url = 'https://mobile-12.firebaseio.com/users/$number.json';
try {
await http.post(
url,
body: json.encode({
'password': password,
'phoneNumber':user.phoneNumber,
}),
);
_status = Status.Authenticated;
} catch (error) {
print(error);
}
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => HomeScreen(
)));
} else {
print("Error");
}
},
verificationFailed: (FirebaseAuthException exceptio) {
print('${exceptio.message} + something is wrong');
});
} catch (e) {
handleError(e, context,number,password);
errorMessage = e.toString();
notifyListeners();
}
notifyListeners();
}
Future<bool> smsOTPDialog(BuildContext context,String number,String password) {
return showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Enter SMS Code'),
content: Container(
height: 85,
child: Column(children: [
TextField(
onChanged: (value) {
this.smsOTP = value;
},
),
(errorMessage != ''
? Text(
errorMessage,
style: TextStyle(color: Colors.red),
)
: Container())
]),
),
contentPadding: EdgeInsets.all(10),
actions: <Widget>[
FlatButton(
child: Text("Confirm"),
textColor: Colors.white,
color: Colors.blue,
onPressed: () async {
final code = this.smsOTP.trim();
AuthCredential credential = PhoneAuthProvider.credential(
verificationId: verificationId, smsCode: code);
UserCredential result =
await _auth.signInWithCredential(credential);
User user = result.user;
if (user != null) {
print('user already exist');
// //TO DO:// Save the phone number and password to DB
print('Adding user to Db in the manual OTP route');
final url = 'https://mobile-12.firebaseio.com/users/$number.json';
try {
await http.post(
url,
body: json.encode({
'password': password,
'phoneNumber':user.phoneNumber,
}),
);
} catch (error) {
print('INSIDE ERROR');
print(error);
}
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setBool("logedIn", true);
logedIn = true;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => HomeScreen(
)));
loading = false;
notifyListeners();
} else {
print("No OTP was added");
loading = true;
notifyListeners();
Navigator.of(context).pop();
}
},
)
],
);
});
}
//Sign-In Method checks to see if a phone exists.
signIn(BuildContext context, String number, String password,AuthMode authMode) async {
try {
//Check to see if the phone number is available
final url = 'https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPhoneNumber?key=';
final response = await http.post(
url,
body: json.encode(
{
'phoneNumber': number
},
),
);
final extractedData = json.decode(response.body) as Map<String, dynamic>;
print('Extracted data is ' + extractedData.toString());
//Register and send OTP if new user
if (extractedData == null && authMode == AuthMode.Signup) {
print('Inside NULL no errors');
// //Verify phone
verifyPhone(context, number, password);
}
//If tries to login but phone not available
else if(extractedData == null && authMode == AuthMode.Login)
{
_showErrorDialog('Phone number does not exist. Please Sign Up', context);
}
else if (extractedData['error'] != null) {
_showErrorDialog('Something went wrong! Please try again!', context);
}
//If someone signup but their phone already exist
else if(extractedData != null && authMode == AuthMode.Signup)
{
_showErrorDialog('Your phone already exists. Please Login!', context);
}
//If available, proceed to homepage
else {
print('User found');
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => HomeScreen(
)));
}
} catch (e) {
handleError(e, context,number,password);
}
}
handleError(error, BuildContext context,String number,String password) {
errorMessage = error.toString();
print('ERROR IS ' + errorMessage);
notifyListeners();
}
Future<bool> _showErrorDialog(String message,BuildContext context) {
return showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: Text('An Error Occurred!'),
content: Text(message),
actions: <Widget>[
FlatButton(
child: Text('Okay'),
onPressed: () {
Navigator.of(ctx).pop();
},
)
],
),
);
}
}

How to show alert on firebase auth errors flutter

I want to show an alert dialog when there is an error in the firebase auth.
Firebase already prints the error in the UI but i want to show a dialog to the user.
Heres my createUser and signInUser Funtion and my signup button function
Future registerWithEmailAndPassword({String email,password,username,image,phoneNumber}) async {
try {
UserCredential userCredential = await _firebaseAuth
.createUserWithEmailAndPassword(
email: email,
password: password,
);
} on FirebaseAuthException catch (e) {
if (e.code == 'weak-password') {
print('The password provided is too weak.');
} else if (e.code == 'email-already-in-use') {
print('The account already exists for that email.');
}
} catch (e) {
print(e);
}
}
Future signInWithEmailAndPassword({String email, String password}) async {
try {
UserCredential userCredential = await _firebaseAuth
.signInWithEmailAndPassword(
email: email,
password: password
);
User user = userCredential.user;
assert(user.uid != null);
email = user.email;
} 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.');
}
}
}
press: () {
if (formKey.currentState.validate()) {
formKey.currentState.save();
context
.read<Authentication>()
.signInWithEmailAndPassword(
email: emailController.text,
password: passwordController.text)
.whenComplete(() => Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) =>
HomeScreen())));
}
},
You can set up an AlertDialog widget similar to this. The Yes/No buttons are probably overkill in your situation and if so, just convert to an ok button and then you don't have to check the return result.
Future<String> showYesNoAlertDialog({
#required BuildContext context,
#required String titleText,
#required String messageText,
}) async {
// set up the buttons
final Widget yesButton = FlatButton(
onPressed: () => Navigator.pop(context, 'yes'),
child: const Text('Yes'),
);
final Widget noButton = FlatButton(
onPressed: () => Navigator.pop(context, 'no'),
child: const Text('No'),
);
// set up the AlertDialog
final alert = AlertDialog(
title: Text(titleText),
content: Text(messageText),
actions: [
yesButton,
noButton,
],
);
// show the dialog
return showDialog(
context: context,
builder: (context) => alert,
);
}
Then where you have your print statements outputting the errors, you'd call the above widget like this
final dr = await showYesNoAlertDialog(
context: context,
titleText: 'Authentication Error',
messageText:
'There has been an error during authentication. Would you like to retry?',
);
if (dr == 'yes') {
// Yes button clicked
}
else {
// No button clicked
}

How to edit a user in flutter firestore

I have been learning how does flutter work with firestore and now I am working in user auth with password, email and username, when a user is created the email and password are saved with an uid but the username and the email(again) are saved in firestore with a different uid, by the way I have tried a lot of things to make it have the same id but I currently cant find the way. in addition to this, there is also a function that is supposed to edit the username and save those changes. The problem comes when trying to implement the edit functinality because the edit form doesnt return anything as an output except the loading screen, I think this error is happening because of the uids. How can I fix this problem?
models/user.dart
class CustomUser {
final String uid;
CustomUser({this.uid});
}
class UserData {
final String uid;
final String name;
UserData({this.uid, this.name});
}
models/username.dart
class Username {
final String name;
Username({this.name});
}
services/auth.dart
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
// create user obj based on fb user
CustomUser _userFromFirebaseUser(User user) {
return user != null ? CustomUser(uid: user.uid) : null;
}
Stream<CustomUser> get user {
return _auth.authStateChanges().map(_userFromFirebaseUser);
}
//signin email password
Future signInWithEmailAndPassword(String email, String password) async {
try {
UserCredential result = await _auth.signInWithEmailAndPassword(
email: email, password: password);
User user = result.user;
return _userFromFirebaseUser(user);
} catch (e) {
print(e.toString());
return null;
}
}
//signup
Future registerWithEmailAndPassword(String email, String password) async {
try {
UserCredential result = await _auth.createUserWithEmailAndPassword(
email: email, password: password);
User user = result.user;
return _userFromFirebaseUser(user);
} catch (e) {
print(e.toString());
return null;
}
}
//signout
Future signOut() async {
try {
return await _auth.signOut();
} catch (e) {
print(e.toString());
return null;
}
}
services/database.dart
class DatabaseService {
final String uid;
DatabaseService({this.uid});
final CollectionReference userCollection = FirebaseFirestore.instance.collection('usernames');
Future updateUserData(String name) async { // this is the function that has to edit the username
return await userCollection.doc(uid).set({
'name': name,
});
}
Future uploadUserInfo(userMap) async { // this function adds username and email to firestore
return await userCollection.doc(uid).set(userMap);
}
List<Username> _usernameListFromSnapshot(QuerySnapshot snapshot) {
return snapshot.docs.map((doc) {
return Username(
name: doc.data()['name'] ?? '',
);
}).toList();
}
// userData from snapshot
UserData _userDataFromSnapshot(DocumentSnapshot snapshot) {
return UserData(
uid: uid,
name: snapshot.data()['name'],
);
}
Stream<List<Username>> get usernames {
return userCollection.snapshots().map(_usernameListFromSnapshot);
}
Stream<UserData> get userData {
return userCollection.doc(uid).snapshots().map(_userDataFromSnapshot);
}
}
register.dart(code that registers the user with a username)
onPressed: () async {
if (_formKey.currentState.validate()) {
setState(() => loading = true);
dynamic result = await _auth.registerWithEmailAndPassword(email, password).then((val) {
Map<String, String> userInfoMap = {
"name": name,
"email": email,
};
databaseService.uploadUserInfo(userInfoMap);
});
if (result == null) {
setState(() {
error = 'please suply a valid email';
loading = false;
});
}
}
}),
editForm.dart
final _formKey = GlobalKey<FormState>();
String _currentName;
final user = Provider.of<CustomUser>(context);
StreamBuilder<UserData>(
stream: DatabaseService(uid: user.uid).userData,
builder: (context, snapshot) {
if (snapshot.hasData) {
UserData userData = snapshot.data;
return Form(
key: _formKey,
child: Column(
children: <Widget>[
Text('edit username!'),
SizedBox(
height: 30,
),
TextFormField(
// initialValue: userData.user gives a initial text to the input
validator: (val) => val.isEmpty ? 'Please enter a name' : null,
onChanged: (val) => setState(() => _currentName = val),
),
RaisedButton(
child: Text('Save'),
onPressed: () async {
if (_formKey.currentState.validate()) {
print('update if good');
await DatabaseService(uid: user.uid).updateUserData(
_currentName ?? userData.name,
);
}
Navigator.pop(context);
})
],
));
} else {
return Loading();
}
},
);
If you have any questions please let me know;)
In your register.dart, the registerWithEmailAndPassword method returns a User object which contains the uid internally created by FirebaseAuth however, it doesn't seem like you took used this uid to update your Firestore user document. I've implemented a sample of what should have been done below.
dynamic result = await _auth.registerWithEmailAndPassword(email, password).then((val) {
Map<String, String> userInfoMap = {
"name": name,
"email": email,
};
DatabaseService(uid: val.uid).uploadUserInfo(userInfoMap);
});
I just realized that your registerWithEmailAndPassword function returns a CustomUser instead of a Firebase User. I just modified it to make it work.
//signup
Future<User> registerWithEmailAndPassword(String email, String password) async {
try {
UserCredential result = await _auth.createUserWithEmailAndPassword(
email: email, password: password);
return result.user;
} catch (e) {
print(e.toString());
return null;
}
}
//editFrom.dart
//form validation function
Map<String, String> userMap = {'name': currentName};
await DatabaseService(uid: user.uid).uploadUserInfo(userMap);
Side note: when working with Futures, it helps if you specify the expected return type as this will help you with debugging. I've done it for the function above

Get user UID after user registering

How to get user UID after user registration. I'm trying to do with getCurrentUser method but it ends up always null. Here is the method :
String userId;
#override
void initState() {
super.initState();
getCurrentUser();
}
getCurrentUser() async {
FirebaseUser firebaseUser = await FirebaseAuth.instance.currentUser();
setState(() {
userId = firebaseUser.uid;
});
}
And here is the button when users wants to register and the data that user input will send to Firebase. But the userId always null.
Container(
height: 45.0,
width: 270.0,
child: RaisedButton(
child: Text('SIGN UP'),
onPressed: () async {
setState(() {
showSpinner = true;
});
try {
final newUser =
await _auth.createUserWithEmailAndPassword(
email: email, password: password);
_firestore.collection('UserAccount').add({
'uid': userId,
'Email Address': email,
'Full Name': nama,
'Phone Number': phoneNumber,
});
if (newUser != null) {
Navigator.pushNamed(context, HomePage.id);
}
setState(() {
showSpinner = false;
});
} catch (e) {
print(e);
}
},
),
),
If I try to register with the code above, this error showed up.
Thank you for anyone who trying to help :(
You need to get the uid after registering:
final newUser =
await _auth.createUserWithEmailAndPassword(
email: email, password: password);
_firestore.collection('UserAccount').add({
'uid': newUser.user.uid,
'Email Address': email,
'Full Name': nama,
'Phone Number': phoneNumber,
});
newUser should return a value of type AuthResult and inside AuthResult it has a field of type FirebaseUser, therefore you can use newUser.user.uid.
If you want to get the data from the document based on the user id, then I recommend to do the following:
final newUser =
await _auth.createUserWithEmailAndPassword(
email: email, password: password);
_firestore.collection('UserAccount').document(newUser.user.uid).setData({
'Email Address': email,
'Full Name': nama,
'Phone Number': phoneNumber,
});

Resources