Flutter Firestore adding data - firebase

I want to add data on firestore and it wont work. can somebody help me.
This is the newest updated version and I can't figure out how...
firebase_auth: ^0.18.0+1 cloud_firestore: ^0.14.0+2
This is the sign up screen so I want to send data after I create the email and password.
I want to add the document with user uid too.
onPressed: () async {
try {
UserCredential userCredential = await FirebaseAuth
.instance
.createUserWithEmailAndPassword(
email: _emailController.text,
password: _passwordController.text,
);
if (userCredential != null) {
firestore
.collection("user")
.doc('user.uid')
.set({
'username': username,
'email': email,
})
.then((value) => print("User Added"))
.catchError((error) =>
print("Failed to add user: $error"));
Navigator.of(context).pushNamed(AppRoutes.authLogin);
}
} catch (e) {
print(e);
_usernameController.text = "";
_passwordController.text = "";
_repasswordController.text = "";
_emailController.text = "";
//TODO: alertdialog with error
}
setState(() {
saveAttempted = true;
});
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
}
},
Can someone help me with the firestore.. Thank you..

First Create a User class.
class UserData {
final String userId;
final String fullNames;
final String email;
final String phone;
UserData(
{this.userId,
this.fullNames,
this.email,
this.phone});
Map<String, dynamic> getDataMap() {
return {
"userId": userId,
"fullNames": fullNames,
"email": email,
"phone": phone,
};
}
}
Then you can use a function like this one to save the credentials and save the data to firestore
createOrUpdateUserData(Map<String, dynamic> userDataMap) async {
FirebaseUser user = await FirebaseAuth.instance.currentUser();
DocumentReference ref =
Firestore.instance.collection('user').document(user.uid);
return ref.setData(userDataMap, merge: true);
}
==
bool validateAndSave() {
final form = _formKey.currentState;
if (form.validate()) {
form.save();
return true;
}
return false;
}
void validateAndSubmit() async {
if (validateAndSave()) {
try {
String userId = _formType == FormType.login
? await widget.auth.signIn(_email, _password)//use your signin
: await widget.auth.signUp(_email, _password);//use your signup
if (_formType == FormType.register) {
UserData userData = new UserData(
fullNames: _fullNames,
email: _email,
phone: "",
);
createOrUpdateUserData(userData.getDataMap());
}
} catch (e) {
setState(() {
_isLoading = false;
switch (e.code) {
case "ERROR_INVALID_EMAIL":
_authHint = "Your email address appears to be malformed.";
break;
case "ERROR_EMAIL_ALREADY_IN_USE":
_authHint = "Email address already used in a different account.";
break;
case "ERROR_WRONG_PASSWORD":
_authHint = "Your password is wrong.";
break;
case "ERROR_USER_NOT_FOUND":
_authHint = "User with this email doesn't exist.";
break;
case "EMAIL NOT VERIFIED":
_authHint = "Email not verified: Please go to yor email and verify";
break;
case "ERROR_USER_DISABLED":
_authHint = "User with this email has been disabled.";
break;
case "ERROR_TOO_MANY_REQUESTS":
_authHint =
"Too many Attemps. Account has temporarily disabled.\n Try again later.";
break;
case "ERROR_OPERATION_NOT_ALLOWED":
_authHint = "Signing in with Email and Password is not enabled.";
break;
case "ERROR_ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL":
_authHint = "The email is in use by another account";
break;
default:
_authHint = "An undefined Error happened.";
}
});
print(e);
errorDialog(context, _authHint);
}
} else {
setState(() {
_authHint = '';
});
}
}
Then use
onpressed:(){
validateAndSubmit();
}
the formtype is an Enum
enum FormType { login, register, reset }
widget.auth.signIn and widget.auth.signUp should be replaced with your signin and signup respectively.
Added a custom error block to differentiate firebase auth errors as well.
Defining an auth page independently will help you reuse your code in future.

Related

Flutter: have different authentication providers in Firebase

I have different methods in my app to log in:
Facebook
Google
Apple
Email
For the question I'll focus on the first 2 ones. When the user logs in with Facebook the providers look like this:
That's fine but if I log out and log in again, this time with a new Google account but using same email, the providers look like this:
Now, if I log out and log in again with Facebook I face the account-exists-with-different-credential error. Something for which I have the logic prepared and show its provider login method, but this user should have both provider available and he should be able to log in with both methods.
This is my code:
Future facebookSignIn(BuildContext context) async {
final LoginResult result = await FacebookAuth.instance.login();
if (result.status == LoginStatus.success) {
final AccessToken accessToken = result.accessToken!;
AuthCredential credential =
FacebookAuthProvider.credential(accessToken.token);
await _firebaseCredential(context, credential);
}
}
Future googleSignIn(BuildContext context,
[String? email, facebookCredential]) async {
try {
GoogleSignInAccount googleUser;
dynamic popup = await _googleSignIn.signIn();
// cancelled login
if (popup == null) {
return null;
}
googleUser = popup;
GoogleSignInAuthentication googleAuth = await googleUser.authentication;
final AuthCredential credential = GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
await _firebaseCredential(context, credential);
} on FirebaseAuthException catch (e) {
// await FirebaseCrashlytics.instance.recordError(
// e,
// StackTrace.fromString("/googleSignIn"),
// reason: e.message,
// );
// return null;
}
}
_firebaseCredential(BuildContext context, credential) async {
try {
User user =
(await FirebaseAuth.instance.signInWithCredential(credential)).user!;
// Provider.of<MyRents>(context, listen: false).updateUI();
await firebaseProfile.updateUserData(context, user);
} on FirebaseAuthException catch (error) {
// final error = e as FirebaseAuthException;
if (error.code == 'account-exists-with-different-credential') {
String email = error.email!;
// AuthCredential pendingCredential = e.credential;
List<String> signInMethods =
await FirebaseAuth.instance.fetchSignInMethodsForEmail(email);
// If the user has several sign-in methods,
// the first method in the list will be the "recommended" method to use.
if (signInMethods.first == 'google.com' ||
signInMethods.first == 'facebook.com') {
// TODO: fix facebook
return await googleSignIn(context, email, credential);
} else {
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text(error.message!)));
}
} else {
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text(error.message!)));
}
}
}
Am I missing something?
flutter_facebook_auth: ^4.3.3
google_sign_in: ^5.2.1
Future googleSignIn(BuildContext context,
[String? email, facebookCredential]) async {
try {
GoogleSignInAccount googleUser;
dynamic popup = await _googleSignIn.signIn();
// cancelled login
if (popup == null) {
return null;
}
googleUser = popup;
GoogleSignInAuthentication googleAuth = await googleUser.authentication;
final AuthCredential credential = GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
await _firebaseCredential(context, credential);
} on FirebaseAuthException catch (e) {
// await FirebaseCrashlytics.instance.recordError(
// e,
// StackTrace.fromString("/googleSignIn"),
// reason: e.message,
// );
// return null;
}
}
Future facebookSignIn(BuildContext context) async {
final LoginResult result = await FacebookAuth.instance.login();
if (result.status == LoginStatus.success) {
final AccessToken accessToken = result.accessToken!;
AuthCredential credential =
FacebookAuthProvider.credential(accessToken.token);
await _firebaseCredential(context, credential);
}
}
// other methods...
_firebaseCredential(BuildContext context, credential) async {
try {
User user =
(await FirebaseAuth.instance.signInWithCredential(credential)).user!;
await firebaseProfile.updateUserData(context, user);
} on FirebaseAuthException catch (error) {
if (error.code == 'account-exists-with-different-credential') {
String email = error.email!;
List<String> signInMethods =
await FirebaseAuth.instance.fetchSignInMethodsForEmail(email);
// bool newUser = (signInMethods.length > 0) ? false : true;
// If the user has several sign-in methods,
// the first method in the list will be the "recommended" method to use.
var user;
switch (signInMethods.first) {
case 'google.com':
user = await googleSignIn(context, email, credential);
break;
case 'facebook.com':
user = await facebookSignIn(context);
break;
case 'apple.com':
user = await appleSignIn(context);
break;
case 'password':
// since password is managed by user we force have email provider only
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(translate('auth.signInMethods_password'))));
break;
// TODO: apple
}
await linkProvider(context, credential);
return user;
}
return ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text(error.message!)));
}
}
// just some extra error covering
Future linkProvider(BuildContext context, credential) async {
try {
await FirebaseAuth.instance.currentUser?.linkWithCredential(credential);
} on FirebaseAuthException catch (e) {
switch (e.code) {
case "provider-already-linked":
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(translate('auth.provider_already_linked'))));
break;
case "invalid-credential":
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(translate('auth.invalid_credential'))));
break;
case "credential-already-in-use":
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(translate('auth.credential_already_in_use'))));
break;
default:
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(translate('auth.something_happened'))));
}
}
}
If you do Google -> Facebook it will look like this:
Other way around only Google will be present if your Google email is a trusted email (gmail). More info about that:
https://groups.google.com/g/firebase-talk/c/ms_NVQem_Cw/m/8g7BFk1IAAAJ

To Register Cloud Firestore with Facebook Input

I can log in with Facebook, but I can not register Cloud Firestore. How do I get the resources to suggest? I can log in with Google, but here I have problems. I've shared the codes below. I'm not receiving an error. The information comes from Facebook but clicking on the button on the try catch process:
Future<Kullanici> facebookIleGiris() async {
print("Login wiht facebook");
FacebookLoginResult _result = await _facebookLogin.logIn(['email']);
switch(_result.status){
case FacebookLoginStatus.cancelledByUser:
print("user logout");
break;
case FacebookLoginStatus.error:
print("error");
break;
case FacebookLoginStatus.loggedIn:
await _loginWithFacebook(_result);
break;
}
}
_loginWithFacebook(FacebookLoginResult _result) async {
FacebookAccessToken accessToken = _result.accessToken;
AuthCredential _credential =
FacebookAuthProvider.getCredential(accessToken: accessToken.token);
AuthResult result = await _firebaseAuth.signInWithCredential(_credential);
print('${result.user.uid} ');
print('${result.user.displayName} now login');
print('${result.user.email} ');
print('${result.user.photoUrl} ');
print('${result.user.phoneNumber}');
return _kullaniciOlustur(result.user);
ButtonClick
void _faceIleGiris() async {
print("user clicked");
var _yetkilendirmeServisi = Provider.of<YetkilendirmeServisi>(context, listen: false);
print("user 2. step");
try {
Kullanici kullanici = await _yetkilendirmeServisi.facebookIleGiris();
print("kullanici kontrole başladi ${kullanici.email}");
if (kullanici != null) {
print(" ${kullanici.email}");
Kullanici fireStoreKullanici = await FirestoreServisi().kullaniciGetir(kullanici.id);
if (fireStoreKullanici == null) {
print("user created ${kullanici.email}");
FirestoreServisi().kullaniciOlustur(
id: kullanici.id,
email: kullanici.email,
kullaniciAdi: kullanici.userName,
fotoUrl: kullanici.fotoUrl
);
print(" ${kullanici.email}");
print("the end");
}
}
}
catch(ex){
print(ex);
}
User.dart
import 'package:flutter/cupertino.d art';
import 'package:cloud_firestore/cloud_firestore. dart';
import 'package:firebase_auth/firebase_auth. dart';
class Kullanici{
final String id;
final String userName;
final String email;
final String fotoUrl;
Kullanici({#required this.id, this.userName, this.email, this.fotoUrl});
factory Kullanici.firebasedenUret(FirebaseUser user){
return Kullanici(id:user.uid,
userName: user.displayName,
email: user.email,
fotoUrl: user.photoUrl
);
}
factory Kullanici.dokumandanuret(DocumentSnapshot doc){
return Kullanici(
id: doc.documentID,
userName: doc['kullaniciAdi'],
email: doc['email'],
fotoUrl: doc['fotoUrl']
);
}
}
createUser method:
//Kullaniciolustur
class FirestoreServisi{
final Firestore _firestore= Firestore.instance;
Future<void> kullaniciOlustur({id,email,kullaniciAdi, fotoUrl=""}) async {
await _firestore.collection("kullanicilar").document(id).setData({
"kullaniciAdi":kullaniciAdi,
"email":email,
"fotoUrl":fotoUrl,
"dTarih":""
});
}
When the FacebookIleGiris () method is logged in with Facebook, the function to be initiated to the initiated function and the error has been solved.
facebookIleGiris() async {
print("facebook ile giriş başladi");
FacebookLoginResult _result = await _facebookLogin.logIn(['email']);
switch(_result.status){
case FacebookLoginStatus.loggedIn:
var a= await _loginWithFacebook(_result);
return a;
break;
case FacebookLoginStatus.cancelledByUser:
print("kullanici çıktı");
break;
case FacebookLoginStatus.error:
print("error");
break;
}
}
_loginWithFacebook(FacebookLoginResult _result) async {
FacebookAccessToken accessToken = _result.accessToken;
AuthCredential _credential =
FacebookAuthProvider.getCredential(accessToken: accessToken.token);
AuthResult result = await _firebaseAuth.signInWithCredential(_credential);
print('${result.user.uid} ');
print('${result.user.displayName} şimdi giriş yaptı');
print('${result.user.photoUrl} ');
print('${result.user.email} ');
print(result.runtimeType);
return _kullaniciOlustur(result.user);

How check if user verify mail and then let logged in?

I know the question was asking a lot of times but after spending hours trying to understand im still don't know how to doing that.
So right know I got 2 Future methods
the SigIn
Future<String> signIN(String email, String password) async {
try {
(await FirebaseAuth.instance.signInWithEmailAndPassword(
email: email.trim(),
password: password,
))
.user;
} on FirebaseAuthException catch (e) {
switch (e.code) {
case 'invalid-email':
{
return 'Email is not valid';
}
case 'user-disabled':
{
return 'Account is not active';
}
case 'user-not-found':
{
return 'No user found';
}
case 'wrong-password':
{
return 'wrong password';
}
default:
{
return 'Unexpected error!';
}
}
}
return null;
}
And the Sign up
Future<String> signUp(String email, String password) async {
try {
(await FirebaseAuth.instance.createUserWithEmailAndPassword(
email: email.trim(),
password: password,
))
.user
.sendEmailVerification();
} on FirebaseAuthException catch (e) {
switch (e.code) {
case 'invalid-email':
{
return 'Email is not valid';
}
case 'user-disabled':
{
return 'Account is not active';
}
case 'user-not-found':
{
return 'No user found';
}
case 'wrong-password':
{
return 'wrong password';
}
default:
{
return 'Unexpected error!';
}
}
}
return null;
}
And i wish I know how to first check if user verified mail and then let him login.
Right know user press register button and automatically login .
Please help!!
Here the on pressed methods
First the pressed register button
onPressed: () async {
if (_formKey.currentState.validate()) {
String authError = await _auth.signUp(email, password);
if (authError != null) {
setState(() => jawoll = true);
setState(() => error = authError);
}else{
setState(() => loading = true);
setState(() => jawoll = false);
setState(() => error = "Email send to $email");
}
}
}
And the sign in in button
onPressed: () async {
if (_formKey.currentState.validate()) {
String authError = await _auth.signIN(email, password);
if (authError != null) {
setState(() => error = authError);
print("olaaa");
print(error);
}
setState(() => loading = false);
}
}
Remove the stream (authStateChanges) from your widget tree that is responsible for automatically logging in the user.
Then, manually push a new screen for email registration. After it's done let your user go to the home screen.
On opening the app again in the future, you can check if the user is logged in and if the email is verified. If so, move to the home screen and if not to the log in screen.
The problem here is that the stream automatically changes on user login, wether the email is verified or not. So, remove it and proceed !!

Flutter - Get Firebase custom claims while writing Firebase user to own user instance

I am trying to implement the example given at How do I access custom claims? to my existing code.
I have a Stream which listens to auth changes and updates my own user object with the responded Firebase user. When I store my user object, I would like to get the custom claims of that user as well.
The problem is in _userFromFirebaseUser.
It says "The await expression can only be used in an async function.
Try marking the function body with either 'async' or 'async*'."
But when I do so, the error is hops to my stream where it then says "The argument type 'Future Function(User)' can't be assigned to the parameter type 'User Function(User)'." for "_userFromFirebaseUser" in
// auth change user stream
Stream<local.User> get user {
return _auth.authStateChanges().map(_userFromFirebaseUser);
}
Here is my complete authentication class:
import 'package:<my-pckg>/models/user.dart' as local;
import 'package:firebase_auth/firebase_auth.dart' as auth;
import 'package:<my-pckg>/services/database.dart';
//import 'package:shared_preferences/shared_preferences.dart';
class AuthService {
final auth.FirebaseAuth _auth = auth.FirebaseAuth.instance;
// create user obj based on firebase user
local.User _userFromFirebaseUser(auth.User user) {
final isAdmin = (await _currentUserClaims)['admin'] == true;
return user != null
? local.User(
uid: user.uid,
email: user.email,
displayName: user.displayName,
isAdmin: isAdmin)
: null;
}
// auth change user stream
Stream<local.User> get user {
return _auth.authStateChanges().map(_userFromFirebaseUser);
}
// sign in anon
Future signInAnon() async {
try {
auth.UserCredential result = await _auth.signInAnonymously();
auth.User user = result.user;
return _userFromFirebaseUser(user);
} catch (e) {
print(e.toString());
return null;
}
}
// sign in with email and password
Future signInWithEmailAndPassword(String email, String password) async {
try {
auth.UserCredential result = await _auth.signInWithEmailAndPassword(
email: email, password: password);
auth.User user = result.user;
print('Successfully logged in, User UID: ${user.uid}');
return user;
} catch (error) {
print(error.toString());
return null;
}
}
// register with email and password
Future registerWithEmailAndPassword(String email, String password) async {
try {
auth.UserCredential result = await _auth.createUserWithEmailAndPassword(
email: email, password: password);
auth.User user = result.user;
// create a new document for the user with the uid
await DatabaseService(uid: user.uid).updateUserData(null);
print('Successfully registered, User UID: ${user.uid}');
return _userFromFirebaseUser(user);
} catch (error) {
print(error.toString());
return null;
}
}
// sign out
Future signOut() async {
try {
print('User signed out');
return await _auth.signOut();
} catch (error) {
print(error.toString());
return null;
}
}
Future<Map<dynamic, dynamic>> get _currentUserClaims async {
final user = _auth.currentUser;
// If refresh is set to true, a refresh of the id token is forced.
final idTokenResult = await user.getIdTokenResult(true);
return idTokenResult.claims;
}
}
Am I heading into the wrong direction? Is there anything obvious, that I simply do not consider?
Thanks for your help!
For those, heading into the same problem, I found the solution after further research:
You will have to change the .map to .asyncMap.
Here is the code, which works for me:
import 'package:<my-pckg>/models/user.dart' as local;
import 'package:firebase_auth/firebase_auth.dart' as auth;
import 'package:<my-pckg>/services/database.dart';
//import 'package:shared_preferences/shared_preferences.dart';
class AuthService {
final auth.FirebaseAuth _auth = auth.FirebaseAuth.instance;
// create user obj based on firebase user
Future<local.User> _userFromFirebaseUser(auth.User user) async {
final isAdmin = (await _userClaims)['admin'] == true;
return user != null
? local.User(
uid: user.uid,
email: user.email,
displayName: user.displayName,
isAdmin: isAdmin)
: null;
}
// auth change user stream
Stream<local.User> get user {
return _auth.authStateChanges().asyncMap(_userFromFirebaseUser);
}
// sign in anon
Future signInAnon() async {
try {
auth.UserCredential result = await _auth.signInAnonymously();
auth.User user = result.user;
return _userFromFirebaseUser(user);
} catch (e) {
print(e.toString());
return null;
}
}
// sign in with email and password
Future signInWithEmailAndPassword(String email, String password) async {
try {
auth.UserCredential result = await _auth.signInWithEmailAndPassword(
email: email, password: password);
auth.User user = result.user;
print('Successfully logged in, User UID: ${user.uid}');
return user;
} catch (error) {
print(error.toString());
return null;
}
}
// register with email and password
Future registerWithEmailAndPassword(String email, String password) async {
try {
auth.UserCredential result = await _auth.createUserWithEmailAndPassword(
email: email, password: password);
auth.User user = result.user;
// create a new document for the user with the uid
await DatabaseService(uid: user.uid).updateUserData(null);
print('Successfully registered, User UID: ${user.uid}');
return _userFromFirebaseUser(user);
} catch (error) {
print(error.toString());
return null;
}
}
// sign out
Future signOut() async {
try {
print('User signed out');
return await _auth.signOut();
} catch (error) {
print(error.toString());
return null;
}
}
Future<Map<dynamic, dynamic>> get _userClaims async {
final user = _auth.currentUser;
// If refresh is set to true, a refresh of the id token is forced.
final idTokenResult = await user.getIdTokenResult(true);
return idTokenResult.claims;
}
}
Found here: In flutter, how can I "merge" Firebase onAuthStateChanged with user.getTokenId() to return a Stream?

I need to add email/password authentication with flutter and firebase

I have a Google authentication working just fine, and I need to create another signUp method (with email/and password), so what are the best approaches for implementing this signUp method?
I tried to create a user and it worked. But then I can't navigate to my other pages!
I couldn't find any good blog posts or documentation. Note that when the user signs up I also need to add them in firebase Database.
Try something like this:
emailPasswordLogin(BuildContext context, String email, String password)async{
try {
AuthResult authResult = await _auth.signInWithEmailAndPassword(
email: email, password: password);
FirebaseUser user = authResult.user;
} catch (ex) {
print("Code: " + ex.code);
switch (ex.code) {
case 'ERROR_USER_NOT_FOUND':
{
await createUser(email, password, context);
}
case 'ERROR_WRONG_PASSWORD':
print("wrong password);
break;
case 'ERROR_INVALID_EMAIL':
print("invalid email")
break;
case 'ERROR_OPERATION_NOT_ALLOWED':
print("Login Method not defined")
break;
case 'ERROR_WEAK_PASSWORD':
print("weak password")
break;
default:
print('Case ${ex.message} is not yet implemented');
}
return;
}
storeData(user, context);
}
storeData(FirebaseUser user, BuildContext context) async {
_database = new FirebaseDatabase();
await _database
.reference()
.child('user')
.push().set(<dynamic, dynamic> async{
"email": user.email,
}).then((_) {
print("Transaction commited");
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => NextPage());
});
}

Resources