So for the beginning I tried to get just the name of the user from Firebase and display it on the home page but it just display the widget's name.
Im using google/facebook/anonymous/email & password login methods.
Firebase instance
Future getCurrentUser() async {
print('done');
return _auth.currentUser;
}
The widget that suppose to get the user details
class GetCurrentUser extends StatefulWidget {
#override
_GetCurrentUserState createState() => _GetCurrentUserState();
}
class _GetCurrentUserState extends State<GetCurrentUser> {
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: Provider.of(context).auth.getCurrentUser(),
builder: (context, snapshot) {
print('done');
if (snapshot.connectionState == ConnectionState.done ) {
return snapshot.data.displayName;
} else {
return Text('error');
}
},
);
}
}
And the String that stores the user's name
String userProfile = GetCurrentUser().toString();
Found a solution!
using the code of this thread's answer How to get the current user id from Firebase in Flutter
I took the void function he made and used it inside the widget I wanted to change the user name too and setState for that string
final FirebaseAuth _ath = FirebaseAuth.instance;
void inputData() {
final User user = _ath.currentUser;
setState(() {
userProfile = user.displayName;
});
}
Related
I'm still learning flutter and ,I want to get a value from Realtime database in firebase and then show it in the screen.
this is the full code , i can see the value in the terminal but when i dont know how to display it on the screen
class Body extends StatefulWidget {
#override
BodyState createState() => BodyState();
}
class BodyState extends State<Body> {
final db = FirebaseFirestore.instance;
late final reference = FirebaseDatabase.instance.ref();
late DatabaseReference databaseReference ;
#override
Widget build(BuildContext context) {
DatabaseReference tempvaleur =
FirebaseDatabase.instance.ref('temperature/esofostemperature0/valeur');
tempvaleur.onValue.listen((DatabaseEvent event) async {
print(event.snapshot.value);
final data = event.snapshot.value ;
}
);
return Column(
);
}
}
You should instead use the StreamBuilder which allows you to consume the real-time stream from a Firebase collection or document.
In your case, your build method would've looked like this:
#override
Widget build(BuildContext context) {
DatabaseReference tempvaleur =
FirebaseDatabase.instance.ref('temperature/esofostemperature0/valeur');
return StreamBuilder(
stream: tempvaleur.onValue,
builder: (context, snapshot) {
if (snapshot.hasData) {
// consume your data here
var data = (snapshot.data! as DatabaseEvent).snapshot.value;
// hoping your value is a string, but just in case...
return Text(data.toString());
}
return CircularProgressIndicator();
}
);
Here i implement firebase login, register, home page setting in my flutter app.
For this i want to set Homepage according to firestore query in main.dart
Here is my code
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
//
return MaterialApp(
debugShowCheckedModeBanner:false,
home: setHomePage(),
);
}
}
setHomePage() {
if(FirebaseAuth.instance.currentUser==null){
return Login();
}
else {
FirebaseFirestore.instance.collection(AppString.FB_USERS).doc(
FirebaseAuth.instance.currentUser.uid).get().then((
DocumentSnapshot snapshot) {
if (snapshot.exists) {
return UserStateJunction();
}
else {
return User();
}
});
}
}
I think this code has no error ,
But this is not work and homepage is not waiting for firestore query , it throws null exception
Could not find a generator for route RouteSettings("/", null) in the
_WidgetAppState. Make sure your root app widget has provided a way to generate this route. Generators for routes are searched for in the following order:
For the "/" route, the "builder" property, if non-null, is used.
Otherwise, the "routes" table is used, if it has an entry for the route.
Otherwise, onGenerateRoute is called. It should return a non-null value for any valid route not handled by "builder" and "routes".
Finally, if all else fails onUnknownRoute is called. Unfortunately, onUnknownRoute was not set.
int main(){
runApp( YourApp() )
}
class YourApp extends StatelessWidget{
#override
Widget build(BuildContext context){
return FutureBuilder<FirebaseUser>(
future: FirebaseAuth.instance.currentUser(),
builder: (BuildContext context, AsyncSnapshot<FirebaseUser> snapshot){
if (snapshot.hasData){
FirebaseUser user = snapshot.data; // this is your user instance
/// is because there is user already logged
return MainScreen();
}
/// other way there is no user logged.
return LoginScreen();
}
);
}
}
This is my Fire Base cloud store structure the data is stored in the document is as Map<string,Dynamic>
What Would be the Query if i want to fetch the username to corresponding uid ?
Also , i want return the username to a text widget
Language Used : Dart
String getUserName (User user) {
String username;
/* Query */
return username;
}
class username extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Text("USER NAME : " + getUserName());
}
}
Please Help!
You can use the FlutterFire Package to read Data from your Firestore
https://firebase.flutter.dev/docs/firestore/usage/#one-time-read
Take a look at their example, you only have to make a few Adjustments:
class GetUserName extends StatelessWidget {
final String documentId; // This is your User UID
GetUserName(this.documentId);
#override
Widget build(BuildContext context) {
CollectionReference users = FirebaseFirestore.instance.collection('users');
return FutureBuilder<DocumentSnapshot>(
future: users.doc(documentId).get(),
builder:
(BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasError) {
return Text("Something went wrong");
}
if (snapshot.connectionState == ConnectionState.done) {
Map<String, dynamic> data = snapshot.data.data();
return Text("USER NAME: ${data['name']}");
}
return Text("loading");
},
);
}
}
I am using Firebase Auth with google sign in Flutter. I am able to sign in however when I close the app(kill it), I have to sign up all over again. So is there a way to persist user authentication till specifically logged out by the user?
Here is my auth class
import 'package:firebase_auth/firebase_auth.dart';
import 'package:google_sign_in/google_sign_in.dart';
class Auth {
FirebaseAuth _firebaseAuth;
FirebaseUser _user;
Auth() {
this._firebaseAuth = FirebaseAuth.instance;
}
Future<bool> isLoggedIn() async {
this._user = await _firebaseAuth.currentUser();
if (this._user == null) {
return false;
}
return true;
}
Future<bool> authenticateWithGoogle() async {
final googleSignIn = GoogleSignIn();
final GoogleSignInAccount googleUser = await googleSignIn.signIn();
final GoogleSignInAuthentication googleAuth =
await googleUser.authentication;
this._user = await _firebaseAuth.signInWithGoogle(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken,
);
if (this._user == null) {
return false;
}
return true;
// do something with signed-in user
}
}
Here is my start page where the auth check is called.
import 'package:flutter/material.dart';
import 'auth.dart';
import 'login_screen.dart';
import 'chat_screen.dart';
class Splash extends StatefulWidget {
#override
_Splash createState() => _Splash();
}
class _Splash extends State<Splash> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: CircularProgressIndicator(
value: null,
),
),
);
}
#override
void initState() {
super.initState();
_handleStartScreen();
}
Future<void> _handleStartScreen() async {
Auth _auth = Auth();
if (await _auth.isLoggedIn()) {
Navigator.of(context).pushReplacementNamed("/chat");
}
Navigator.pushReplacement(context, MaterialPageRoute(builder: (BuildContext context) => LoginScreen(auth: _auth,)));
}
}
I believe your problem is routing. In my apps I use FirebaseAuth and it works just as you say you wanted to, and I don't persist any login token. However, I don't know why your approach of using a getUser is not working.
Try to adjust your code to use onAuthStateChanged. EDIT: As of 2022, with Flutter 3, I noticed it worked better with userChanges instead.
Basically, on your MaterialApp, create a StreamBuilder listening to _auth.userChanges() and choose your page depending on the Auth status.
I'll copy and paste parts of my app so you can have an idea:
[...]
final FirebaseAuth _auth = FirebaseAuth.instance;
Future<void> main() async {
FirebaseApp.configure(
name: '...',
options:
Platform.isIOS
? const FirebaseOptions(...)
: const FirebaseOptions(...),
);
[...]
runApp(new MaterialApp(
title: '...',
home: await getLandingPage(),
theme: ThemeData(...),
));
}
Future<Widget> getLandingPage() async {
return StreamBuilder<FirebaseUser>(
stream: _auth.userChanges(),
builder: (BuildContext context, snapshot) {
if (snapshot.hasData && (!snapshot.data!.isAnonymous)) {
return HomePage();
}
return AccountLoginPage();
},
);
}
Sorry, it was my mistake. Forgot to put the push login screen in else.
Future<void> _handleStartScreen() async {
Auth _auth = Auth();
if (await _auth.isLoggedIn()) {
Navigator.of(context).pushReplacementNamed("/chat");
}
else {
Navigator.pushReplacement(context, MaterialPageRoute(builder: (BuildContext context) => LoginScreen(auth: _auth,)));
}
}
void main() {
FirebaseAuth.instance.authStateChanges().listen((User user) {
if (user == null) {
runApp(MyApp(auth : false);
} else {
runApp(MyApp(auth : false);
}
});
}
class MyApp extends StatefulWidget {
final bool auth;
MyApp({this.auth});
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
return MaterialApp(
......
......
home: widget.auth ? MainScreen() : AuthScreen();
);
You can use shared_preferences to keep alive your session even when you kill the app.
Here is the documentation https://pub.dartlang.org/packages/shared_preferences.
Also I've heard that it's possible to use sqlite to persist the session.
Add this code. It should work fine.
FirebaseAuth auth = FirebaseAuth.instance;
auth.setPersistence(Persistence.SESSION);
You can use my code, You can use userChanges() instead of authStateChanges()
Notifies about changes to any user updates.
This is a superset of both [authStateChanges] and [idTokenChanges]. It provides events on all user changes, such as when credentials are linked, unlinked and when updates to the user profile are made. The purpose of this Stream is for listening to realtime updates to the user state (signed-in, signed-out, different user & token refresh) without manually having to call [reload] and then rehydrating changes to your application.
final Stream<User?> firebaseUserChanges = firebaseAuth.userChanges();
One more simple example:
Future<bool> isUserLoggedIn() async {
final User? user = FirebaseAuth.instance.currentUser;
return user != null;
}
class InitialScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder<bool>(
future: isUserLoggedIn(),
builder: (_, snapshot) {
if (snapshot.hasData) {
if (snapshot.data ?? false) {
Navigator.of(context).push(MaterialPageRoute(builder: (context) => UnauthScreen()));
} else {
Navigator.of(context).push(MaterialPageRoute(builder: (context) => HomeScreen()));
}
}
return const Center(child: CircularProgressIndicator());
},
),
);
}
}
I was able to achieve it by checking the firebase instance currentUser value. if null I routed to my Signup page. If not, then I routed to my HomePage. Not sure if there is anything wrong with this implementation (its working well so far) but seems simpler than the StreamBuilder solution posted above.
home: getLandingPage(),
routes: {
(...)
}
Widget getLandingPage() {
if (_auth.currentUser == null) {
return SignupPage();
} else {
return HomePage();
}
}
App Flowchart
I have a question about async function in flutter. I write an that use Firebase authentication. I want to make it such that the app will read the Firebase User ID at the top level of the app(Root Page in this case) at the init state function and then pass the user object to its child widget. Since the function to retrieve the user ID is an async function, I run into problem that the child widget get a null value for user ID even though it should not be null. I have already use future builder in the children widget but it doesn't work. Does anyone know how to do it correctly.
The exact error I am getting is "A build function returned null. The offending widget is: FutureBuilder. Build functions must never return null."
RootPage (Parent)
class _RootPageState extends State {
AuthStatus authStatus = AuthStatus.notSignIn;
String cuerrentUserId;
#override
void initState() {
super.initState();
widget.auth.currentUser().then((userId) {
setState(() {
authStatus = userId == null ? AuthStatus.notSignIn : AuthStatus.signIn;
cuerrentUserId = userId;
});
});
}
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new FutureBuilder<FirebaseUser>(
future: FirebaseAuth.instance.currentUser(),
builder: (BuildContext context, AsyncSnapshot<FirebaseUser> snapshot) {
switch(authStatus) {
case AuthStatus.notSignIn:
return new LoginPage(
auth: new Auth(),
onSignedIn: _signedIn,
);
case AuthStatus.signIn:
if (snapshot.connectionState == ConnectionState.done) {
return new HomePage(
auth: widget.auth,
onSignedOut: _signedOut,
userId: snapshot.data.uid,
);
}
else {
}
}
}
),
);
}
HomePage (child)
Future<String> setUserData() async {
currentUser = User(widget.userId);
await currentUser.loadUserData();
_userName = currentUser.name;
_userEmail = currentUser.email;
_userPicURL = currentUser.avatar;
print('current user');
print(currentUser.id);
print(currentUser.email);
return _userName;
}
#override
Widget build(BuildContext context) {
return UserProvider(
user: currentUser,
child: new Container(
child: new FutureBuilder(
future: setUserData(),
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
if (snapshot.data!=null) {
...
You could make your main function async in order to decide during app launch if you should show the login or home page as the first screen.
This could look like the following:
Future<void> main() async {
FirebaseUser currentUser = await FirebaseAuth.instance.currentUser();
bool showHomePage = currentUser != null;
runApp(MyApp(showHomePage));
}
You could use the showHomePage param inside MyApp now to determine which screen should be shown initially. That's it.
Bonus: With this approach you also don't need to show a screen for a friction of a second which may be replaced by another one (e.g. show the home screen --> user is not logged in --> replace with login screen). This could look like a glitch in your app.