Future<Map> returnUserMap() async {
final FirebaseUser currentUser = await _auth.currentUser();
Map userMap = {
"UserName": currentUser.displayName,
"UserEmail": currentUser.email,
"UserUrl": currentUser.photoUrl
};
print("1");
print(userMap);
return userMap;
}
return value type is Instance of 'Future>'.
I want to get a UserName, how can I do it?
Your function returnUserMap() returns a Future<Map>. I suspect that the error you describe is not in the code snippet you copied.
Whenever the task to be performed may take some time, you will receive a future. You can wait for futures in an async function with await.
It is therefore recommended to use a so-called FutureBuilder in your build() function:
FutureBuilder<FirebaseUser>(
future: _auth.currentUser(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
final FirebaseUser user = snapshot.data;
if (user.displayName == null || user.displayName.isEmpty())
return text(currentUser.email); // display email if name isn't set
return text(currentUser.displayName);
}
if (snapshot.hasError) {
return text(snapshot.error);
}
return text('loading...');
},
),
If you want to have the displayName outside your build() function, the following code should do the job when you are inside of an async function:
final FirebaseUser user = await _auth.currentUser();
final String displayName = user.displayName;
print('the displayName of the current user is: $displayName');
And this code when you are in a normal function:
_auth.currentUser().then((FirebaseUser user) {
String displayName = user.displayName;
print('displayName: $displayName');
}).catchError((error) {
print('error: ' + error.toString());
});
I think it's worth watching the following video for further understanding:
Async/Await - Flutter in Focus
Related
I have created a group screen in my flutter app and when I hit the create button it create a group as described is code:
//Create group
Future<String> createGroup(String groupName1, userUid) async {
String retVal = "error";
List<String> members = [];
try {
members.add(userUid);
DocumentReference docRef;
docRef = await firestore.collection("groups").add({
'name': groupName1,
'leader': userUid,
'members': members,
'groupCreate': Timestamp.now(),
});
retVal = "success";
} catch (e) {
// ignore: avoid_print
print(e);
}
return retVal;
}
I am stuck in getting the snapshot from my FBbackend. I want to get the info and display is on my group screen. How can I achieve that?
I have try to get the snapshot but it says Text("..."). I think the problem is that the value of DocumentReference? docRef; is empty, but I donโt know how to fixed it.
code:
DocumentReference? docRef;
docRef == null
? const Text('Error No Group Name Found!!')
: // ๐ handle null here
StreamBuilder<DocumentSnapshot<Map<String, dynamic>>>(
stream: FirebaseFirestore.instance
.collection('groups')
.doc(docRef!.id)
.snapshots(),
builder: (context, snapshot) {
if (snapshot.data == null) {
return const ErrorScreen();
}
return Center(
child: Text((snapshot.data
as DocumentSnapshot<
Map<String, dynamic>>)['name']),
);
}),
I am trying to log a user in with the boolean value assigned to the 'isVerified' field in the user's firestore document.
In other words, If 'isVerified' is true then continue, else return to verify page.
I put in debugPrint statements to help me catch the error and it appears that the Future Builder is not getting past the builder context. I have read other documentation to regarding future builders but I can't find where I'm going wrong, please let me know if there's anything I can clarify. Thank you
Using Future Builder for async
FutureBuilder (
future: getVerified(),
builder: (context, snapshot) { <--------- Nothing past this line is running
debugPrint('>> Home: FutureBuilder: checkpoint'); // does not print to console
if (snapshot.hasData && !snapshot.hasError) {
debugPrint('>> Home: FutureBuilder: Snapshot has data and no error');
}
return const Text('');
}
);
Future
Future<bool> getVerified() async {
debugPrint('>> Home: getVerified Started');
User? user = auth.currentUser;
await FirebaseFirestore.instance
.collection('users')
.doc(user!.uid)
.get()
.then((value) {
bool isVerified = value.data()!['isVerified'];
debugPrint('>> Home: getVerified $isVerified'); // this variable is currently true or false
return isVerified; // this will return Instance of '_Future'
});
return false;
}
You don't need to change FutureBuilder it is good. And I recode your getVerified() function.
Can you try
Future<bool> getVerified() async {
debugPrint('>> Home: getVerified Started');
bool isVerified = false; // set your response to false
// get your user
final user = FirebaseAuth.instance.currentUser;
// check the data from firestore if the user is not null
if (user != null) {
final docSnapShot = await FirebaseFirestore.instance
.collection('users')
.doc(user.uid)
.get();
if (docSnapShot.exists) {
isVerified = docSnapShot.data()!['isVerified'];
}
}
debugPrint(
'>> Home: getVerified $isVerified'); // this variable is currently true or false
return isVerified; // this will return Instance of '_Future'
}
FirebaseFirestore.instance.collection('users').doc(user.uid).where('your filed', isEqualTo: 1).get();
try this
Future<bool> getVerified() async {
debugPrint('>> Home: getVerified Started');
User? user = auth.currentUser;
if(user != null) {
final value = await FirebaseFirestore.instance
.collection('users')
.doc(user!.uid)
.get();
bool isVerified = value.data()!['isVerified'];
return isVerified;
} else {
return false;
}
}
don't use promises in future method use async await
you are using function that returns bool value, FutureBuilder future cannot able to get the data. Try to donot use bool.
Future<bool> getVerified() async {
debugPrint('>> Home: getVerified Started');
User? user = auth.currentUser;
await FirebaseFirestore.instance
.collection('users')
.doc(user!.uid)
.get()
.then((value) {
bool isVerified = value.data()!['isVerified'];
debugPrint('>> Home: getVerified $isVerified'); // this variable is currently true or false
return isVerified; // this will return Instance of '_Future'
});
return false;
}
So I'm trying to save data into firestore, but I have a class to signup which has this code:
FirebaseAuth auth = FirebaseAuth.instance;
await auth.createUserWithEmailAndPassword(
email: emailController.text, password: passwordController.text)
.then((value) => {
Navigator.pushNamed(context, 'DialogFlow'),
user=auth.currentUser,
user.sendEmailVerification(),
DatabaseService(uid:user.uid).UpdateUserData("", emailController.text, ChatScreenState().mess)
this will navigate me to the dialogflow, which has this code:
#override
Widget build(BuildContext context) {
final firebaseUser = context.watch<User>();
FirebaseAuth auth = FirebaseAuth.instance;
user=auth.currentUser;
DatabaseService db = DatabaseService(uid: user.uid);
return StreamBuilder(
stream: db.userData,
builder: (context , snapshot){
Userdata userdata=snapshot.data;
print("====================================");
print(snapshot.data);
print("====================================");
if (userdata != null) {
this.userTestMessage = "";
shhh = pressed ? true : false;
flag = true;
if (!Retrieved_messages) {
this.messsages = userdata.messsages; //Retrieve user data from firebase only once.
// Retrieve user data from firebase only once.
Retrieved_messages = true;
}//load only 20 messages at once . When we scroll up load more.
db.UpdateUserData(
firebaseUser.displayName, firebaseUser.email, this.messsages);
print(userdata.messsages);
print(mess);
print(userdata.messsages);
print("==============================");
print(snapshot.data);
print("==============================");
}
if (db.getUserMessages() == null) {
if (firebaseUser != null) {
db.UpdateUserData(
firebaseUser.displayName, firebaseUser.email, this.messsages);
}
}
and the database class is
DatabaseService({this.uid, this.messsages});
final CollectionReference userCollection = FirebaseFirestore.instance
.collection('users');
UpdateUserData(String Username, String Email,
List messsages) async
{ try {
return await FirebaseFirestore.instance.collection("users").doc(uid).set({
'Username': Username,
'Email': Email,
'messsages': messsages,
}
);
}catch(e){
print(e+" this is the error");
}
}
Future getUserMessages() async
{
FirebaseFirestore.instance.collection(uid).snapshots();
}
Userdata _userDataFromSnapshot(DocumentSnapshot snapshot) {
return Userdata(uid: uid,
name: snapshot.data()['Username'],
email: snapshot.data()['Email'],
messsages: snapshot.data()['messsages']
);
}
Stream<Userdata> get userData {
return userCollection.doc(uid).snapshots().map(_userDataFromSnapshot);
}
}
The problem I'm facing is that whenever I create a new user, and try to save new messages, the firestore keeps on saving and removing the messages, so its in an infinite loop, so I hope that someone can tell me how I can fix it that it saves the messages without removing them.
Note: the snapshot data isn't updating.
I believe the reason you have your build function rebuilt is usage of context.watch in the beginning of your build(). As in bloc documentation:
Using context.watch at the root of the build method will result in the entire widget being rebuilt when the bloc state changes. If the entire widget does not need to be rebuilt, either use BlocBuilder to wrap the parts that should rebuild, use a Builder with context.watch to scope the rebuilds, or decompose the widget into smaller widgets.
From the below code i cant access the userid of current state user without await.If i need to use await like below i need to enclose the code in a method so that there will be async. But, as the code is enclosed in a method i cant access the userid variable in databaseReference.
Can you help:
somemethod() async{
FirebaseUser userid = await FirebaseAuth.instance.currentUser();
}
final **databaseReference** = FirebaseDatabase.instance.reference().child("UserProfile").child(userid.uid).("Favorites");
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: **databaseReference**.once(),
builder: (context, AsyncSnapshot<DataSnapshot> snapshot) {
if (snapshot.hasData) {
List<Map<dynamic, dynamic>> list = [];
for (String key in snapshot.data.value.keys) {
list.add(snapshot.data.value[key]);
}
From this code I want userid enclosed in method to be accessed in databaseReference so that I can use databaseReference in FutureBuilder to retrieve the user information.
Thank You.
If you want to have only 1 future builder, you need to pass function paramether on databaseReference.once() method
class yourDatabase {
Future<dynamic> once(Function somemethod) async{
//Now, you are caling the funcion on yourDatabase class
dynamic returnOfSomethod = await somemethod();
//TODO your somemethod here
}
}
and to call you make this
somemethod() async{
FirebaseUser userid = await FirebaseAuth.instance.currentUser();
}
//Inside the build...
FutureBuilder(
future: **databaseReference**.once(somemethod), // ! Dont place ()
builder: () {}
)
hope it works!
FirebaseUser userId;
Future databaseReference;
somemethod() async{
userid = await FirebaseAuth.instance.currentUser();
databaseReference = FirebaseDatabase.instance.reference().child("UserProfile").child(userid.uid).("Favorites"); }
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: **databaseReference**.once(),
builder: (context, AsyncSnapshot<DataSnapshot> snapshot) {
if (snapshot.hasData) {
List<Map<dynamic, dynamic>> list = [];
for (String key in snapshot.data.value.keys) {
list.add(snapshot.data.value[key]);
}
I have created a StreamController to handle authentication. I subscribe a user when the sign in is completed. So I create a class for that:
class AuthAPI {
final FacebookLogin facebookLogin = FacebookLogin();
final Dio _dio = Dio();
final StreamController<User> _authStatusController = StreamController<User>.broadcast();
Stream<User> get onAuthStatusChanged => _authStatusController.stream;
// Facebook Sign In
Future<User> facebookSignIn() async {
FacebookLoginResult result = await facebookLogin.logIn(['public_profile', 'email']);
switch(result.status) {
case FacebookLoginStatus.loggedIn:
return _sendFacebookUserDataToAPI(result);
case FacebookLoginStatus.error:
return null;
case FacebookLoginStatus.cancelledByUser:
print('Cancelled');
return null;
default:
return null;
}
}
// Sign Out
void signOut() async {
facebookLogin.logOut();
_authStatusController.sink.add(null);
_authStatusController.close();
}
Future<User> _sendFacebookUserDataToAPI(FacebookLoginResult result) async {
final String facebookToken = result.accessToken.token;
final Response graphResponse = await _dio.get(
'https://graph.facebook.com/v4.0/me?fields='
'first_name,last_name,email,picture.height(200)&access_token=$facebookToken');
final profile = jsonDecode(graphResponse.data);
ApiProvider apiProvider = ApiProvider();
UserSocialAuth userSocialAuth = UserSocialAuth(
firstName: profile['first_name'],
lastName: profile['last_name'],
email: profile['email'],
provider: 'facebook',
providerUserId: profile['id']
);
Map socialSignIn = await apiProvider.socialSignIn(userSocialAuth);
User user;
if (socialSignIn.containsKey('access_token')) {
Map userData = await apiProvider.currentUser(socialSignIn['access_token']);
user = User.fromJson(userData['data']);
apiProvider.setAccessToken(socialSignIn['access_token']);
_authStatusController.sink.add(user);
print("Login Successful");
} else {
_authStatusController.sink.addError(socialSignIn['error']);
}
_authStatusController.close();
return user;
}
}
and this is my StreamBuilder:
return StreamBuilder(
stream: userBloc.authStatus,
builder: (BuildContext context, AsyncSnapshot snapshot) {
print(snapshot.connectionState);
switch(snapshot.connectionState) {
case ConnectionState.active:
User user = snapshot.data;
if (user == null) {
return SignInSignUpScreen();
}
return _showHomeUI(user, snapshot);
case ConnectionState.done:
User user = snapshot.data;
if (user == null) {
return SignInSignUpScreen();
}
print(user);
return _showHomeUI(user, snapshot);
default:
return Center(child: CircularProgressIndicator());
}
}
);
So, when I make the login, then it shows a CircularProgressIndicator, and if the authentication is successful, then it has to show the home screen. But, it stills showing the login screen, and when I print the output of the connectionState, I see that after the connectionState.done, the connectionState pass to connectionState.waiting and I do not know why.
Here is the output of the console:
And when it reaches to the last connectionState.done, it does not have data.
You're calling _authStatusController.close(); in the end of _sendFacebookUserDataToAPI method โ that means that the underlying stream is finished and you stream listener enters "done" state.
You should instead create e.g. dispose() method in AuthAPI class and call _authStatusController.close() there. This method should be called when AuthAPI is no longer needed.