Future Builder is Not Building - flutter

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;
}

Related

Flutter - Null check operator used on a null value

I am trying to retrieve user data using a DocumentSnapshot. However I am getting a casterror because the instance that I created is pointing to null. So I tried calling the method getUserProfileData in the initState method to see if it could assign a value to my DocumentSnapshot instance but I still get that error. Please may anyone kindly help.
//DocumentSnapshot instance that is null
DocumentSnapshot? userDocument;
getUserProfileData() {
FirebaseFirestore.instance
.collection('users')
.doc(FirebaseAuth.instance.currentUser!.uid)
.snapshots()
.listen((event) {
userDocument = event;
});
}
#override
void initState() {
getUserProfileData();
//This is where the castError occurs because userDocument is null
nameController.text = userDocument!.get('name');
userNameController.text = userDocument!.get('username');
try {
descriptionController.text = userDocument!.get('description');
} catch (e) {
descriptionController.text = '';
}
try {
followers = userDocument!.get('followers').length;
} catch (e) {
followers = 0;
}
try {
following = userDocument!.get('following').length;
} catch (e) {
following = 0;
}
}
It would be better to use StreamBuilder for firstore-snapshot.
late final stream = FirebaseFirestore.instance
.collection('users')
.doc(FirebaseAuth.instance.currentUser!.uid)
.snapshots();
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: stream,
builder: (context, snapshot) {....},
);
}
Also error can be bypass like
getUserProfileData() {
FirebaseFirestore.instance
.collection('users')
.doc(FirebaseAuth.instance.currentUser!.uid)
.snapshots()
.listen((event) {
userDocument = event;
/// all those stuff here
nameController.text = userDocument!.get('name');
userNameController.text = userDocument!.get('username');
..........
Try use async/await for getUserProfileData() function
getUserProfileData() async {
await FirebaseFirestore.instance
.collection('users')
.doc(FirebaseAuth.instance.currentUser!.uid)
.snapshots()
.listen((event) {
userDocument = event;
});
}
#override
void initState() {
getUserProfileData();
// Replace with default value case userDocument is null
nameController.text = userDocument!.get('name') ?? "";
userNameController.text = userDocument!.get('username') ?? "";
try {
descriptionController.text = userDocument!.get('description') ?? "";
} catch (e) {
descriptionController.text = '';
}
try {
followers = userDocument!.get('followers').length ?? 0;
} catch (e) {
followers = 0;
}
try {
following = userDocument!.get('following').length ?? 0;
} catch (e) {
following = 0;
}
}
Async determines that a method will be asynchronous, that is, it will not return something immediately, so the application can continue executing other tasks while processing is not finished.
await serves to determine that the application must wait for a response from a function before continuing execution. This is very important because there are cases where a function depends on the return of another.
a ?? b means that if the value of a is null, the value b will be assigned

How to get snapshot from groupScreen? flutter firebase

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']),
);
}),

How to read List of Instance of '_JsonQuerySnapshot' in Flutter

I am trying to read the data from a list that has the data of Instance of '_JsonQuerySnapshot'.
In the below code I am trying to get the data from DB
getData() async {
var db = FirebaseFirestore.instance;
var categoryList = [];
db.collection('categories')
.doc(value)
.get()
.then((DocumentSnapshot doc) {
var listSubCol = doc["categoryCollection"];
listSubCol.forEach((id) {
db.collection('categories')
.doc(value)
.collection(id)
.get()
.then((snapshot) {
categoryList.add(snapshot);
return categoryList;
});
});
});
}
Below is the code where I am printing the values.
FutureBuilder(
future: getData(),
builder: (BuildContext context, snapshot){
if (snapshot.hasError) {
return Text("Something went wrong");
}
if (snapshot.connectionState == ConnectionState.done) {
print('Data: ${snapshot.data}');
return Text("Text");
}
return Text("Text Outside");
}
)
I got a null value.
Can anyone please help me with how to get the data?
The getData is a Future<void>, so when the FutureBuilder is completed the snapshot.data is always null.
Change getData to return a List of whatever you are getting.
Future<List<Categories>> getData() async {
var db = FirebaseFirestore.instance;
final List<Categories> categoryList = [];
await db.collection('categories')
.doc(value)
.get()
.then((DocumentSnapshot doc) {
var listSubCol = doc["categoryCollection"];
listSubCol.forEach((id) {
db.collection('categories')
.doc(value)
.collection(id)
.get()
.then((snapshot) {
categoryList.add(snapshot);
return categoryList;
});
});
});
return categoryList;
}
getData() async {
var db = FirebaseFirestore.instance;
await db.collection('categories').doc(value).get().then((DocumentSnapshot doc) {
var listSubCol = doc["categoryCollection"];
listSubCol.forEach((id) {
db.collection('categories').doc(value).collection(id).get().then((snapshot) {
snapshot.docs.forEach((element) {
categoryList.add(element.data());
});
});
return categoryList;
});
});
return categoryList;
}
using the above method i got the data from Firestore.

How to get inside data in Future<Map<dynamic, dynamic>>?

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

Why I getting another connectionState type after connectionState.done in a StreamBuilder?

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.