I am facing the following problem: I am displaying the username of the google account of the user in a subtitle in a list tile, but the code that I use displays an error if the user is not logged in first, so how can I edit this code to display you are not logged in when the user is not logged in or is logged out. Also, how to display the username of the google account if the user is signed in or changed his account this is the code:
subtitle: new FutureBuilder<FirebaseUser>(
future: FirebaseAuth.instance.currentUser(),
builder: (BuildContext context,AsyncSnapshot<FirebaseUser> snapshot){
if (snapshot.connectionState == ConnectionState.waiting) {
return new Text(snapshot.data.displayName);
}
else {
return new Text('you are not logged in');
}
},
Fixing the problem
You can simply replace snapshot.connectionState == ConnectionState.waiting with snapshot.hasData, which is the equivalent of snpashot.data != null. This, however, will display 'you are not logged in' even when still waiting. I added a 'loading' Text for waiting:
builder: (BuildContext context, AsyncSnapshot<FirebaseUser> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Text('loading');
} else if (!snapshot.hasData) {
return Text('you are not logged in');
} else {
return Text(snapshot.data.displayName);
}
}
This works because currentUser() returns null if there is no current user.
A suggestion
You are currently using currentUser(), which does not update on authentication changes. You can use onAuthStateChanged, which is a stream that will update every time and always provide you with the latest user. For this, you will have to migrate to StreamBuilder:
subtitle: StreamBuilder(
stream: FirebaseAuth.instance.onAuthStateChanged,
builder: (BuildContext context, AsyncSnapshot<FirebaseUser> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Text('loading');
} else if (!snapshot.hasData) {
return Text('you are not logged in');
} else {
return Text(snapshot.data.displayName);
}
},
)
Related
Since I have updated flutter and Android Studio, I am getting an error with a code which was working fine previously. I have done some research but have not find the right solution. Please, can you help me to solve this? Many thanks
if (snapshot.connectionState == ConnectionState.done) {
return StreamBuilder(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
User user = snapshot.data; // error A value of type 'Object?' can't be assigned to a variable of type 'User'.
if (user == null) {
return LoginPageTest();
``
You should have activated null safety, so User user is declared to not accept null, but snapshot.data can be null so is incompatible. You can declase user as nullable, adding a ?, like bellow:
User? user = snapshot.data;
You have to define StreamBuilder data type. By default it Object? (with null safety). Here is your updated code, try this
if (snapshot.connectionState == ConnectionState.done) {
return StreamBuilder<User?>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
User? user = snapshot.data;
if (user == null) {
return LoginPageTest();
``
it doesnt matter if the user is logged in or not it goes to the main page.
anf if i make the login page the home page everytime i restart the app it requires to login again.
i want it to be like once log in then be logged in till you log out
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MaterialApp(
debugShowCheckedModeBanner: false,
home: await getLandingPage(),
routes: {
'upload': (context) => ItemInput(),
'suzuki': (context) => Suzuki(),
'others': (context) => Others(),
},
));
}
Future<Widget> getLandingPage() async {
final FirebaseAuth _auth = FirebaseAuth.instance;
return StreamBuilder<User>(
stream: _auth.authStateChanges(),
builder: (BuildContext context, snapshot) {
if (snapshot.hasData && (!snapshot.data.isAnonymous)) {
return MainPage();
}
return LoginPage();
},
);
}
When the app is started, Firebase automatically restores the user's authentication state. This may take a few moments, as it needs to check with the server whether the account is still active.
During this time, the user will not be signed in yet, so authStateChanges() fires a null. And that's when your code redirects the user to the login page.
You'll want to either wait for a few moments to see if the user state is restored, or move/copy your navigation logic to the login page, so that it redirects to the main page once the user authentication state is restored.
Following on from Frank's answer, this is how to work around the issue of receiving a null on the first authStateChanges() event using a StreamBuilder widget.
#override
Widget build(BuildContext context) {
return StreamBuilder<User>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (BuildContext context, AsyncSnapshot<User> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return _buildWaitingScreen();
default:
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
final firebaseUser = snapshot.data;
if (firebaseUser != null) {
//....
}
return SignInPage();
}
}
});
}
If you don't want to use a switch statement, you can check only ConnectionState.active
#override
Widget build(BuildContext context) {
return StreamBuilder<User>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (BuildContext context, AsyncSnapshot<User> snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
//...
}
return _buildWaitingScreen();
});
}
According to the docs:
A stream A source of asynchronous data events.
A Stream provides a way to receive a sequence of events. Each event is either a data event, also called an element of the stream, or an error event, which is a notification that something has failed. When a stream has emitted all its event, a single "done" event will notify the listener that the end has been reached.
Since the authStateChanges() returns a Stream then you can use the StreamBuilder to get the result and display the widgets accordingly.
According to the StreamBuilder docs:
As an example, when interacting with a stream producing the integers 0 through 9, the builder may be called with any ordered sub-sequence of the following snapshots that includes the last one (the one with ConnectionState.done):
new AsyncSnapshot.withData(ConnectionState.waiting, null)
new AsyncSnapshot.withData(ConnectionState.active, 0)
new AsyncSnapshot.withData(ConnectionState.active, 1)
...
new AsyncSnapshot.withData(ConnectionState.active, 9)
new AsyncSnapshot.withData(ConnectionState.done, 9)
Therefore the builder of type AsyncWidgetBuilder which is used for asynchronous operation, will call your widgets according to the state of the Stream, for example:
#override
Widget build(BuildContext context) {
final FirebaseAuth _auth = FirebaseAuth.instance;
return new Scaffold(
body: StreamBuilder(
stream: _auth.authStateChanges(),
builder: (context, AsyncSnapshot<FirebaseUser> snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
if (snapshot.hasData)
return MainPage();
else
return LoginPage();
} else
return Center(
child: CircularProgressIndicator(),
);
}));
}
You can use the above code in the splash screen, here the Stream will be in the waiting state where it will display a loading first, and then when it retrieves the data, if it is either null or if there is a user logged in, it will enter the active state and return a widget which satisfies the condition.
https://api.flutter.dev/flutter/widgets/AsyncWidgetBuilder.html
https://api.flutter.dev/flutter/widgets/StreamBuilder-class.html
https://api.flutter.dev/flutter/widgets/StreamBuilder/builder.html
After talking with OP. They are using the following plugin google_sign_in, and have an auth.dart file with the following code:
void signOutGoogle() async {
await googleSignIn.signOut();
}
What happened in that case, is that the user signed out from Google auth but was still logged in inside Firebase, so to solve this you can add:
void signOutGoogle() async {
await googleSignIn.signOut();
await _auth.signOut();
}
So I have a Flutter app with Firebase authentication. I have a dart file for sign in and registration and then a separate dart file with different Widgets for a list of data. I want to get the current user so I can get that users personalized data from the database. But for some reason I can never get that user. Here is the code:
Future getUser() async {
final FirebaseUser user = await FirebaseAuth.instance.currentUser();
return user.getIdToken();
}
return FutureBuilder(
future: getUser(),
builder: (context, snapshot) {
if (snapshot.hasData) return Text(snapshot.data);
else if (snapshot.hasError) return Text("data error (see futurebuilder)");
return Text("Await for data");
},
);
But on my debugging device the Text output just says data error (see future builder) which is the error message I wrote. However I can't figure out what I'm doing wrong. I don't see why this isn't just giving me the user ID token. Thanks.
Try changing your code from snapshot.hasData to snapshot.connectionState == ConnectionState.done and see if this helps you. I have the exact same setup in my app and this works for me.
something like this should work
FutureBuilder(
future: getUser(),
builder: (context, snapshot) {
if(snapshot.connectionState == ConnectionState.done){
return
//Execute code after future is returned
} else {
return CircularProgressIndicator();
}
},
);
so my problem is that I'm trying to load a screen after logging in with an user (firebase auth), this works fine. When logged in, I'm matching the users id from firebase auth, with the same id in firebase cloud, such that I can retrieve the data field "work_title" and display it directly in a Text widget.
So of what i can read off the internet, supposedly everything in the screen is drawn first, only then its possible to retrieve the auth id of the current user, thats why I'm using a futurebuilder.
My question now: why can't I return the return Text(sh.data['work_title'].toString());-part ? I do enter this part of the code, however it only returns the return Text("??");-part.
I've also attached an image of this.
Thanks :)
Widget buildText2() {
return FutureBuilder(
future: FirebaseAuth.instance.currentUser(),
builder: (context, AsyncSnapshot<FirebaseUser> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: Text("Loading ..."),
);
} else {
if (snapshot.hasData) {
print(snapshot.hasData); // prints true, so we enter this part:
DocumentReference doc = Firestore.instance
.collection("user_profiles")
.document(snapshot.data.uid);
doc.get().then((sh) {
if (sh.exists) {
print(sh.exists); // prints true, så vi enter here as well:
print(sh.data['work_title'].toString());
return Text(sh.data['work_title'].toString()); // <-- I want to return this
}
});
}
} return Text("??");
});
}
A screenshot of the code
Maybe you can try the following (unfortunately i cannot test it..):
FutureBuilder(
future: FirebaseAuth.instance.currentUser(),
builder: (context, AsyncSnapshot<FirebaseUser> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: Text("Loading ..."));
} else {
if (snapshot.hasData) {
print(snapshot.hasData); // prints true, so we enter this part:
DocumentReference doc = Firestore.instance
.collection("user_profiles")
.document(snapshot.data.uid);
return FutureBuilder(
future: doc.get(),
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
if (snapshot.data.exists) {
return Text(snapshot.data.data['work_title'].toString()); // <-- I want to return this
} else {
return Text('sh does not exists..');
}
} else {
return Text('Still Loading...');
}
},
);
}
}
return Text("??");
})
I'm new to flutter and i'm using firebase auth as my authentication to the application. I'm from a php background and do I need to check if the user is logged in every new page like we check state in php. I'm using this method to handle auth,
Widget handleCurrentScreen() {
return StreamBuilder<FirebaseUser>(
stream: FirebaseAuth.instance.onAuthStateChanged,
builder: (BuildContext context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Scaffold(
appBar: AppBar(
title: Text("Splash"),
),
body: Text("Splash"),
);
} else {
if (snapshot.hasData) {
return HomePage(_auth,_googleSignIn);
}
return LoginScreen3();
}
}
);
}
My question is here once I'm in this page HomePage(), Do I need to verify auth as a good practice or to prevent from hackers?