I am developing a flutter firebase app, which will work for both retailer and admin user. When a retailer will download the app, he/she has to provide phone number auth details and all other mandatory details and has to submit. Admin will get the request and once admin approves, retailer will be able to login and will be able to see products. The admin app has extra features of creating retailers if a retailer is new to the app. Admin wants to add retailer from his app then Admin should provide retailer's phone auth and all other retailer details and user in firebase needs to be created.
Problem here is: Admin is giving the retailer's phone number and after giving OTP, the user for a retailer is getting created but after closing the admin app without signout, if the admin app is opened then the admin app shows Retailer details which was recently created by the admin, maybe because of cache.
I tried signing out of created user just after user is created but i could not find any method to signout specific users from app.
i expect the functionality of creating multiple firebase auth users from a single app.
Please find below the code Snippet.
String _userId = "";
void _signInWithPhoneNumber() async {
setState(() {
_isLoading = true;
});
final AuthCredential credential1 = PhoneAuthProvider.getCredential(
verificationId: _verificationId,
smsCode: _smsCodeController.text,
);
try {
final FirebaseUser user1 =
(await db1.signInWithCredential(credential1));
setState(() {
_isLoading = false;
_errorMessage = 'OTP verified Successfully !!';
if (user1 != null) {
print("Pranay Kumar value of newly created user ${user1.uid}");
db1.signOut();
getCurrentUser().then((user) {
setState(() {
if (user != null) {
_userId = user?.uid;
print("Hello Pranay current Admin User $_userId");
}
});
});
print("Pranay Kumar value of newly created user 222 ${user1.uid}");
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
CreateCustomerDetailedPage(
userId: user1.uid,
phoneNumber: _phoneNumberController.text)));
//getUserDetails(user.uid);
} else {
_isLoading = false;
_errorMessage = 'Sign in failed';
}
});
} catch (e) {
print('Error: $e');
setState(() {
_isLoading = false;
_errorMessage = e.message;
});
}
}
Future<FirebaseUser> getCurrentUser() async {
FirebaseUser user = await FirebaseAuth.instance.currentUser();
return user;
}
After newly created user value of print statement becoming null which i does not want "Hello Pranay current Admin User"
Related
I am trying to implemente facebook signin in flutter, however, firebase does not create a 'uid'. Doesn't the firebase create a uid automatically?
it returns:
The getter 'uid' was called on null.
Receiver: null
Tried calling: uid
below is the sign in method:
Future<UserCredential> signInWithFacebook(BuildContext context) async {
final LoginResult result = await FacebookAuth.instance.login();
if(result.status == LoginStatus.success) {
final OAuthCredential credential = FacebookAuthProvider.credential(result.accessToken.token);
return await FirebaseAuth.instance.signInWithCredential(credential)
.then((user) async {
final graphResponse = await http.get(Uri.parse(
'https://graph.facebook.com/v2.12/me?
fields=name,picture,email&access_token=${result
.accessToken.token}'));
final Map profile = jsonDecode(graphResponse.body);
if (profile != null){
authService.createUser(name: name, email: email, dob: dob, sex: sex);
}
return user;
});
}
Navigator.pushReplacement(context, MaterialPageRoute(
builder: (context) => Profile()));
return null;
}
The sign in method returns a facebook alert dialog requesting the permission to share email, when press continue red screen with the error appears. why is the firestore not creating the user? Thanks! I am not familiar with the system and just learning.
create user method in authServices:
Future<bool> createUser(
{String name,
User user,
String email,
String password,
String phone,
String sex,
String dob}) async {
var res = await firebaseAuth.createUserWithEmailAndPassword(
email: '$email',
password: '$password',
);
if ((res.user != null)) {
await saveUserToFirestore(name, res.user, email, dob, phone, sex);
return true;
} else {
return false;
}
}
As far as I can understand your code you first login the user with Facebook and then again create a new user with createUserWithEmailAndPassword. If you use the same email for both the second one will fail and give you null.
To track the auth state for all providers use the onAuthStateChanged listener:
firebase.auth().onAuthStateChanged((user) => {
if (user) {
// User is signed in, see docs for a list of available properties
// https://firebase.google.com/docs/reference/js/firebase.User
var uid = user.uid;
// ...
} else {
// User is signed out
// ...
}
});
More about it here.
Well I'm using the code from Firebase and the in the user object the photoURL which being returned is General facebook profile with no image
which is generic profile picture
Future signInWithFacebook() async {
try {
// Trigger the sign-in flow
final result = await FacebookAuth.instance.login();
// Create a credential from the access token
final FacebookAuthCredential facebookAuthCredential =
FacebookAuthProvider.credential(result.token);
// Once signed in, return the UserCredential
UserCredential res = await FirebaseAuth.instance.signInWithCredential(facebookAuthCredential);
User user = res.user;
//create a new document for the user with the uid
await UserProfileDatabaseService(uid: user.uid).updateUserData(
user.displayName,
user.email,
user.emailVerified,
user.phoneNumber,
user.isAnonymous
, {'helper': true},
250
); return user;
} catch (e) {
print(e.toString());
return null;
}
}
you can use the following code to display the Facebook profile picture URL after a successful login:
await FacebookAuth.instance.login();
final user = await FacebookAuth.instance.getUserData();
Image.network(user["picture"]['data']['url']);
This code logs the user in via FacebookAuth, retrieves their user data, and displays their profile picture using the Image.network widget.
My question is how to find out if a user who is currently signed in has been authenticated using firebase manual sign in or google sign in?
When I tap into the user property of class FirebaseUser , when I try to access the providerID, it returns "Firebase" even though I am currently signed in through the Google Sign In provider through Firebase. So, is there any way to find out what provider the user has used for registration of the app?
Thanks a lot for your help. Below is the code that I have already written.
Future<FirebaseUser> getFirebaseUser() async {
FirebaseUser user = await _auth.currentUser();
print(user.email);
return user;
}
#override
void initState() {
// TODO: implement initState
super.initState();
startTimer();
}
void startTimer() {
timer = Timer.periodic(Duration(seconds: 2), (timer) {
if (getFirebaseUser() != null) {
// I Need to push to the menu screen while providing the parameters so that it can recognise if the user is from Firebase Manual Auth or google sign in provider.
//print(_user.providerId);
// Navigator.push(context, MaterialPageRoute(
// builder: (context)
// {
// SideBarLayoutStateful(app: MenuScreen(), isFromGoogleSignIn: ,resultUser: _user, profilePicture: _user.photoUrl,);
// }
//));
}
Navigator.pushNamed(context, 'welcome');
timer.cancel();
});
}
I Need to push to the menu screen while providing the parameters so that it can recognise if the user is from Firebase Manual Auth or google sign in provider, which effects the content displayed on the menu screen.
Thanks for your help and I appreciate it!
Is this supposed to happen:
Future<FirebaseUser> getFirebaseUser() async {
FirebaseUser user = await _auth.currentUser();
print(user.email);
print(user.providerId);
return user;
}
Future<void> signUpWithGoogle() async {
try {
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;
return user;
} catch (error) {
print(error);
}
}
I have done that above code to sign in with google and called the function of google sign button like this
signUpWithGoogle().then((value) => Navigator.of(context).push(MaterialPageRoute(builder: (_) {
return HomePage();
})));
But on first time when apk is installed normally the app is asking for choosing the google account
But after log out when i tap on the google SignIn button it is not asking in pop up menu to select the account.
And one more problem is their on clicking on the google signin button firsts it goes to the HomePage() then signIn is hapenning.
#override
void initState() {
super.initState();
getCurrentUser();
}
Future<void> getCurrentUser() async {
FirebaseUser user = await _auth.currentUser();
bool result = await facebookSignIn.isLoggedIn;
if (user != null && user.isEmailVerified == true) {
print("Email");
Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (_) {
return HomePage();
}));
}
}
Is this is the correct method to navigate to the HomePage() for those users who is signed in when app starts
Whenever user logout and sign in again with google account android is smart enough to provide google account directly to the app without giving sign in pop up
for your second problem plz ref this answer -:
Why might a function not get called in initState(){ super.initState()} in flutter, but work perfectly fine if called later in the same page?
first of all i am also new to flutter so this might not be the best solution however this is what i implement in my app for the authentication part
first i created a User class that contain an ID for the user
then i created a stream of user to my app so the app will always be provided with this value and what ever change happen to it in my services this is the code
final FirebaseAuth _auth = FirebaseAuth.instance;
Stream<FirebaseUser> user; // firebase user
User _userFromFireBaseUser(FirebaseUser user) {
return user != null ? User(uid: user.uid) : null;
}
// //auth change user stream
Stream<User> get userStream {
return _auth.onAuthStateChanged.map(_userFromFireBaseUser);
// or we can use .map((FirebaseUser user) => _userFromFireBaseUser(user) );
}
in my main widget
StreamProvider<User>.value(
lazy: false,
value: AuthService().userStream,
child: MaterialApp()//your main widget
then i created a statless wrapper class that read the stream value and according move to a page, in my app i used the wrapper to go to the sign in if the user is null else go to the profile page, in your application i guess it will go to the homepage
class ProfileWrapper extends StatelessWidget {
#override
Widget build(BuildContext context) {
final user = Provider.of<User>(context);
//print(user.uid);
if (user == null) {
print('no user');
return SignUpPage();
} else if (user != null) {
print('there is user');
print(user.uid);
return ProfilePage();
// print('there is user' + user.displayname);
// print('there is user' + user.photourl);
}
}
}
also you need to add the provider package in your pubspec.yaml file
provider: ^4.1.1
by doing this you don't need to handle any navigation between the home and the sign up, if your user is signed in you will automatically be navigated to the home page.
another solution if that is not what you are looking for, after the google sign function finishes check if the firebase user is not null, if there is a user navigate to your homepage
So i am making a simple sign up and login screens in flutter application that uses phone authentication of firebase. For sign up im able to register new user, as the user provides his phone number and gets OTP. But for login i wanna check if the entered number is already registered. If so he gets otp and logs in or if not registered then asks to sign up first.
Firebase admin SDK supports this. Here's how to set up firebase admin (documentation). After you set up admin, you can use cloud_functions package to call APIs from the firebase admin SDK and the API we'll be using is one that allows us to get a user by phone number (documentation). If the API response is a user record, we know a phone exists.
In this example, I'm using node.js. In functions/index.js:
exports.checkIfPhoneExists = functions.https.onCall((data, context) => {
const phone = data.phone
return admin.auth().getUserByPhoneNumber(phone)
.then(function(userRecord){
return true;
})
.catch(function(error) {
return false;
});
});
In your dart code:
final HttpsCallable callable = CloudFunctions.instance.getHttpsCallable(functionName: 'checkIfPhoneExists');
dynamic resp = await callable.call({'phone': _phone});
if (resp.data) {
// user exists
}
Once the OTP is sent to the user you can verify if the user is a new user or an existing one in verify OTP function
verifyOtp(String input, context) async {
String retVal = "error";
OurUser _user = OurUser();
print(input);
final AuthCredential credential = PhoneAuthProvider.credential(
verificationId: _verificationId, smsCode: input);
try {
// await _auth.signInWithCredential(credential);
UserCredential _authResult = await _auth.signInWithCredential(credential);
// Here i have to save the details of the user in the database
if (_authResult.additionalUserInfo.isNewUser) {
currentUser.uid = _authResult.user.uid;
currentUser.phone = _inputText;
currentUser.type = "Customer";
retVal = await OurDatabase().createUser(currentUser);
} else {
// get the information of the user from the database this already exists
currentUser = await OurDatabase().getUserInfo(_authResult.user.uid);
if(currentUser!= null) {
Navigator.pushNamedAndRemoveUntil(
context, "/homescreen", (route) => false);
}
}
print("End of the await");
// when signup with the otp
if (retVal == "success") {
print("why not inside this mane");
Navigator.pushNamedAndRemoveUntil(
context, "/homescreen", (route) => false);
}
saveAllData();
} catch (e) {
print(e);
print("Something went wrong");
//prin
}
}
Now this is when you want to verify OTP from the user and after the top is verified you can know if the user was indeed a new user or an old one but what if you wanted to know that beforehand then the best possible solution would be to create a new collection in the firestore that would have only one document(so you are charged only for one document read) that would just contain all the numbers of the users that are registered within your application,
I used a simple straight forward way and it worked just fine.
First, add the mobile number to the firebase database in a separate node when the user creates the account.
await dbref.child("RegisteredNumbers").push().set({
"phoneNo": FirebaseAuth.instance.currentUser!.phoneNumber,
});
whenever a user tries to log in or signup check in this node if the provided number is available in It or not.
Future<bool> checkNumberIsRegistered({required String number}) async {
bool isNumberRegistered = false;
try {
await dbref.child("RegisteredNumbers").once().then((data) {
for (var i in data.snapshot.children) {
String data = i.child("phoneNo").value.toString();
if (number == data) {
isNumberRegistered = true;
return isNumberRegistered;
} else {
isNumberRegistered = false;
}
}
});
return isNumberRegistered;
} catch (e) {
return false;
}
}
Hope it helps