I am new to Flutter/Firebase and I want to program an app where a user can login/register and then he needs to create a profile with his information like his name, age... and only if he has created his profile, he should be able to continue and see the "main part" of the app.
I already implemented the Firebase Auth with a working Login / Register Page, but my question is now, how to create the Profile thing the most efficent.
At the moment I created this method here at my own:
Future checkUserProfile() async{
// get snapshot from document
final snapShot = await Firestore.instance.collection('profiles').document(uid).get();
if(snapShot == null || !snapShot.exists){
User.gotProfile = false;
} else {
User.gotProfile = true;
}
This method is checking if an user-profile with the Firebase Auth User UID already exists and if not, the user will be send to the "ProfilePage" with a FutureBuilder executing the method above and if it already exists, he will see the main part of the app.
As I already said, I tried it by myself and I wanted to ask if this is already an good implementation or is there even an easier & better way to do it?
Yes this is an good implementation. In my app I have the check User method like yours. The following method is an example. When the user is not registered he forwarded to the RegisterPage else he forwarded to the MainPage.
checkUserAlreadyExists(FirebaseUser user) async {
final userData = await Firestore.instance.collection('users').document(user.uid).get();
if (userData == null || !userData.exists) {
setState(() {
Navigator.pushAndRemoveUntil(context,
MaterialPageRoute(builder: (BuildContext context) => RegisterPage()), ModalRoute.withName('/'));
});
} else {
setState(() {
Navigator.pushAndRemoveUntil(context,
MaterialPageRoute(builder: (BuildContext context) => MainPage()), ModalRoute.withName('/'));
});
}
}
Related
i have created in Flutter the following Code:
if (_auth.currentUser != null) {
&& FirebaseAuth.instance.currentUser.reload() != null
Timer(
Duration(seconds: 3),
() => Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(
builder: (context) =>
HomeScreen(username: _auth.currentUser.displayName)),
(Route<dynamic> route) => false),
);
} else {
Timer(Duration(seconds: 4),
() => Navigator.pushReplacementNamed(context, "/auth"));
}
}
The Problem is actually that the Part && FirebaseAuth.instance.currentUser.reload() != null is not working.
Do you now why? I want to reaload the currentUser-Firebase-AuthState every time the App is openend.
Thanks for helping!!!
The reload() method is asynchronous and returns a Future<void>. You should use the await keyword to wait for a future to complete, see here.
It is not 100% clear to me why you need to use the reload() method. Since you use the displayName property in your code, is it because , in your app, this property is frequently changing? I would kindly suggest that you read in this SO answer the explanations on why one should use this method: "Calling reload() reloads that user's profile data from the server".
So, if it appears that you really need to use this method, you could do something along the following lines:
void navigate() async {
if (_auth.currentUser != null) {
await _auth.currentUser.reload();
// continue your business logic here
// For example
if (_auth.currentUser.isEmailVerified) {
// Navigate to ...
}
} else {...}
}
Note that, as explained in the FlutterFire doc, you could alternatively use the userChanges() method: "This stream provides realtime updates to the User class without having to call reload(), such as when credentials are linked, unlinked and when the user's profile is updated".
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;
}
My goal for this app is to have persistent log in, so that the user of my Flutter app needs to sign in only once (unless they sign out) and whenever my Flutter app restarts, they do not need to log back in. I saw many examples with using Firebase Authentication and the .currentUser() method, but I am still having to log in every time I restart the app. I have tested this on the simulator (ios) and on my physical iphone while running debug mode on xCode (usb connection). Is it possible that I cannot test this functionality this way? Because I do see a message pop up on both android studio and xCode terminals that mention lost connection to device or stopped running because of the restarting. If that's the case, how can I test that persistent log in is working?
If that isn't the case, what am I doing wrong?? I've included the code below. This is happening within a stateful widget, of course.
final _auth = FirebaseAuth.instance;
checkIfCurrentUserExists() async {
try {
await _auth.currentUser().then((user) {
print('this is the user $user');
if (user != null && user.email != null) {
userIsLoggedIn = true;
// this works fine!
}
});
} catch (e) {
print('current user was not found $e');
// this works fine!
}
}
// called inside initState()
setClientOnLoad() async {
await Spryte.checkIfCurrentUserExists();
var doesCurrentUserExist = userIsLoggedIn;
var currentUser = await returnCurrentUser();
if (doesCurrentUserExist == false) {
//if user is not authenticated, set anonymous user
// this works fine!
}
else {
//print('current user does exist');
await foo(currentUser.uid);
// 'foo' is meant to retrieve some data about the client on loading of the app
// so that the user doesn't have to log in every time the app restarts
// but for some reason it's not working for me
}
}
I have got the same problem. I was able to sign in but when restarting the app, I was getting another random UID.
It might sound stupid, but make sure you are not calling signInAnonymously(); at any point in your app without checking if there is already a current user.
This was my problem, I was always signing in anonymously at every app restart.
Here is what I did from my starting app page (which takes care of setting up the app including the user):
Future<FirebaseUser> signInAnonymously() async {
AuthResult result = await _auth.signInAnonymously();
final FirebaseUser user = result.user;
assert(user != null);
assert(await user.getIdToken() != null);
return user;
}
And here is the checking method:
Future<FirebaseUser> tryToFetchUser() async {
var user = await _auth.currentUser();
if (user == null) {
user = await FirebaseAuth.instance.onAuthStateChanged.first;
}
if (user == null) {
user = await signInAnonymously();
}
return Future.value(user);
}
I hope it will help some of you and avoid wasting time on stupid mistakes as I did!
You r calling setClientOnLoad() which is async inside init() method & other app navigation depends on this method so u need wrap all your async stuff in FutureBuilder().
if not then build() method ll be called before complete execution of setClientOnLoad()
e.g. :-
FutureBuilder(
future: setClientOnLoad(),
builder: (context, AsyncSnapshot<R> snapshot) {
if (snapshot.data == null) {
return Center(child: CircularProgressIndicator());
}
// after executing method completely
},
);
I am using firebase in my flutter app and accessing the logged in user would be done something like this -
Future<FirebaseUser> getLoggedInUser() async {
return await FirebaseAuth.instance.currentUser();
}
I use BLOC and was thinking of adding this to my AuthenticationBloc class. Since this BLOC is injected early, I should be able to access this from anywhere in the app -
AuthenticationBloc authBloc = BlocProvider.of<AuthenticationBloc>(context);
FirebaseUser user = await authBloc.getLoggedInUser()
But this leads to a few problems like accessing this in a StreamBuilder becomes an issue because of the need of using await which means I would need to make the StreamBuilder async (not sure how).
What would be the best/cleanest way of accessing the user object FirebaseUser user from anywhere in the app ?
Thanks !
Edit 1 :
So one way to do this would be to use a stateful widget every time I want the current user
class _MyWidgetState extends State<MyWidget> {
FirebaseUser user;
#override
void initState() {
super.initState();
_updateCurrentUser();
}
void _updateCurrentUser() async {
FirebaseUser currUser = await Firebase.instance.currentUser();
setState(() {
user = currUser;
});
}
But using a stateful widget every time I want the currUser seems weird. Is there a better way out ?
You can access the user by using stream such as
StreamBuilder(
stream:FirebaseAuth.instance.onAuthStateChanged,
builder:(context,snapshot){
if (snapshot.connectionState == ConnectionState.active) {
final user = snapshot.data;
return Text("User Id:${user.uid}");
}else{
return Center(
child:CircularProgressIndicator(),
);
}
}
);
I am trying to logout user and switch the widgets but after calling the following function -
void logoutUser() async {
await FirebaseAuth.instance.signOut();
}
If I check for the current user, it's returning user object but with null id -
FirebaseAuth.instance.currentUser()
I try to kick the user out to main.dart after logout which checks if user is signed in or not and loads a correct widget. Does anyone have any idea why currentUser() isn't returning null after calling signOut()?
_auth.currentUser() is probably returning an anonymous FirebaseUser object. Check the isAnonymous property.
Example :
auth.currentUser().then((user) {
if (user == null || user.isAnonymous) {
// what will you do?
return;
}
setState(() {
_uid = user.uid;
});
});
Yet, I would highly recommend to monitor the onAuthStateChanged stream instead. This way you will be informed when the user logs in or logs out immediately.
Check this article, it covers it in depth.