Flutter Firestore boolean value check - flutter

Well, I want to check if the profile is complete after creating the account so I added a bool to the firestore. When the user fills in all the data and clicks "complete" at the end, then bool "complete" will be true and I did it, but now I want to check before the user starts filling in the data if bool is true or false. If this is true, the user will be redirected to the dashboard, if it is false, he will have to complete all the data after logging in. User login details are stored in firebase and the rest of the information is stored in firestore.
If any more information is needed, I will try to specify it
I would like to check if the value is true or false before redirecting to "CreateProfile1 ();", if it's possible
class MainPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder<User?>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: ((context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
} else if (snapshot.hasError) {
return Center(child: Text('Something went wrong!'));
} else if (snapshot.hasData) {
return CreateProfile1();
} else {
return AuthPage();
}
}),
));
}
}
I was trying to save bool value into variable, but i've got this error
external static Never _throw(Object error, StackTrace stackTrace);
Here is this var, final actually
final complete = FirebaseFirestore.instance
.collection('usersdData')
.doc(FirebaseAuth.instance.currentUser!.uid)
.get()
.then((value) {
if ((value.data() as dynamic)['complete'] == true) {
return true;
} else {
return false;
}
});

Related

How to navigate to the Home Page after a successful Login using Flutter (Dart)

I have a Login Controller that does the action of loging in (with Google)
class LoginController extends GetxController {
final _googleSignin = GoogleSignIn();
var googleAccount = Rx<GoogleSignInAccount?>(null);
login() async {
googleAccount.value = await _googleSignin.signIn();
}
logout() async {
googleAccount.value = await _googleSignin.signOut();
}
}
Then I have a Log In UI that shows everything
final controller = Get.put(LoginController());
LoginIn({
super.key,
required String title,
});
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Sign-In Page')),
body: Center(
child: Obx(() {
if (controller.googleAccount.value == null) {
return loginButton(); //the Login button to log in with google
} else {
return seeProfile(); //after Login you are then able to see your profile
}
}),
));
}
And then ofcourse I have a HomePage
How do I go to the Home Page after a successful log in
I tried to use ChatGPT and I did watch a couple of Youtube Videos but their code is ofcourse different then my and that makes it hard to implement their solution
A cleaner method would be to use Stream builder and subscribe to the auth stream in the main.dart file. Based on the value, navigate the user.
'''
StreamBuilder<User?>(
initialData: null,
// stream: RepositoryProvider.of(context).authStream(),
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (!snapshot.hasData &&
snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (snapshot.hasData) {
return HomePage();
}
return Container();
});
I think checking the example in the google_sign_in's package on pub.dev might help you achieving what you want.
In summary, I would suggest you to listen to changes to the user in the initState method and push a new page after signIn is successful:
#override
void initState() {
super.initState();
StreamSubscription<GoogleSignInAccount?> subscription = _googleSignIn.onCurrentUserChanged.listen((GoogleSignInAccount? account) {
if (account != null) {
//push new screen
Navigator.of(context).popAndPushNamed(...)
subscription.cancel()
}
});
}
I couldn't test this code, but it should give you a general idea on how to proceed. Again, check the link above for more information.

Nested StreamBuilders Flutter

So I'm currently using this nest of two streams, one to listen for AuthStateChanges, to know if the user is logged in, and another that listens to a firebase document snapshot request, to know if the user has already setup is account or not.
My problem is that the latter StreamBuilder(_userStream) only runs if the firts one runs, meaning that the only way for my _userStream to run is if the user either logs in or logs out(authStateChanges Stream).
This is inconvinient because after the user creates an account(moment where i run Auth().createUserWithPasswordAndEmail()), I need the user to go throw the process of seting up the account, and only after that the user can acess the mainPage. Only in the end of seting up the account theres a button to "Create Account", which changes the "HasSetupAccount" parameter in firebase to true. But because of the nested Streams problem, the app doesn't go to the mainPage until I force update it.
I hope my question is not as confusing as it looks :)
class _WidgetTreeState extends State<WidgetTree> {
#override
//construtor da class?
Widget build(BuildContext context) {
return StreamBuilder(
stream: Auth().authStateChanges,
builder: (context, snapshot) {
if (snapshot.hasData) {
return StreamBuilder(
stream: _userStream(),
builder:
((context, AsyncSnapshot<DocumentSnapshot> userSnapshot) {
if (userSnapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
} else {
Map<String, dynamic> userData =
userSnapshot.data!.data() as Map<String, dynamic>;
print(userSnapshot.data!.data().toString());
if (userData['HasSetupAccount'] == true) {
return MyHomePage();
} else {
return AccountSetup();
}
}
}));
} else {
return LoginPage();
}
},
);
}
Stream<DocumentSnapshot<Map<String, dynamic>>> _userStream() {
return FirebaseFirestore.instance
.collection('Users')
.doc(Auth().currentUser!.uid)
.snapshots();
}
}

Check if collection has been created and navigate using FutureBuilder Firebase Firestore Flutter

class TestData extends StatelessWidget {
const TestData({super.key});
#override
Widget build(BuildContext context) {
final uid = FirebaseFirestore.instance.collection('users').doc();
return FutureBuilder(
future: FirebaseFirestore.collection("user").doc(uid).get(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return AddProfileScreen();
} else {
if (snapshot.hasError) {
return Center(
child: Text('An error occured'),
);
}
if (snapshot.hasData && !snapshot.data!.exists) {
return AddProfileScreen();
}
if (snapshot.hasData) {
return MainPage();
} else
return CircularProgressIndicator();
}
},
);
}
}
im trying to make a one time form after registration that create the collection and then after user fill all the forms they directed to mainscreen().
i don't know how to make it, any suggestions?
i already make the form and can store the data into collection but i want to validate if user already has collection data they immediately directed to main screen so the form become one time only.

StreamBuilder snapshot is inactive on creating own stream

It is a firebase authentication system with email verification. I am using 2 streams, one is firebase authstatechanges and one of my own to check mail verification status.
I am using a stream combiner for combining 2 streams.
On combining 2 streams and calling the function in stream builder, the snapshot was returning an inactive state. It was not even entering the method "combinestream". Then I tried checking my own stream and passed it to streambuilder alone this time. I found out that it was giving the same problem.
Here is my stream controller code with 2 streams combined:
class AuthService {
final userVerificationStreamController = StreamController<bool>.broadcast();
final auth.FirebaseAuth _firebaseAuth = auth.FirebaseAuth.instance;
MyUser? _userFromFirebase(auth.User? newUser) {
if (newUser == null) {
return null;
}
print("^^^^^------?>>>>>>>>> ${newUser.uid} ,, ${newUser.email} ,, ${newUser.emailVerified}");
return MyUser(newUser.uid, newUser.email,newUser.emailVerified);
}
Stream<CombineStreamer> get combineStream {
Stream<MyUser?> firebaseAuthStream = _firebaseAuth.authStateChanges().map(_userFromFirebase);
Stream<bool> userVerificationStream = userVerificationStreamController.stream;
print("######___>>>>>>>>>> COMBINING STREAMS");
return firebaseAuthStream.combineLatest(userVerificationStream, (p0, p1){
if(p0==null){
userVerificationStreamController.sink.add(false);
}
else if(p0.isEmailVerified){
userVerificationStreamController.sink.add(true);
}
if (p1.toString().toLowerCase() == 'true') {
print("______+++++++___****** ${p1.toString().toLowerCase()}");
return CombineStreamer(p0,true);
} else{
print("______+++++++___>>>>>> ${p1.toString().toLowerCase()}");
return CombineStreamer(p0,false);
}
}
.....
....
}
I called this combineStream method in wrapper class like:
Widget build(BuildContext context) {
final authService = Provider.of<AuthService>(context, listen: false);
return StreamBuilder(
stream: authService.combineStream,
builder: (BuildContext context, AsyncSnapshot snapshot){
if(snapshot.connectionState == ConnectionState.active){
print("!!!!___--->>>>> THE STATE IS ACTIVE");
CombineStreamer? streamedData = snapshot.data;
if(streamedData!=null) {
if(streamedData.user == null || !streamedData.isUserVerified){
print("!!!!___--->>>>> GOING TO LOGIN PAGE");
return LoginPage();
}
else{
print("!!!!___--->>>>> GOING TO HOMEPAGE");
return HomePage();
}
}
else{
print("!!!!!!###### SNAPSHOT IS NULL");
return const Scaffold(body: CircularProgressIndicator(color: Colors.red,),);
}
}
else{
print("!!!!___--->>>>> SHOWING CIRCULAR INDICATOR");
return const Scaffold(body: Center(child: CircularProgressIndicator(),));
}
},
);
}
Here it ALWAYS shows me the circular progress indicator of blue color the default one, where the snapshot is inactive. I am not able to figure out what's causing this problem. Please look into this.
And add the comment if you think something is missing. Thanks!

Futurebuilder is not updating data from firestore

So i am having issue with futurebuilder i want my app to update when a bool is set true but it wasn't working at all so i added a line to to see if the value of bool is changing or not and released it's not changing.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:databse_web_test/database_services/getsocials.dart';
import 'package:flutter/material.dart';
import 'database_services/request.dart';
class RequestWidget extends StatefulWidget {
RequestWidget({Key? key}) : super(key: key);
#override
State<RequestWidget> createState() => _RequestWidgetState();
}
class _RequestWidgetState extends State<RequestWidget> {
String Doc = "EobkN9fONF4IxmpErB1n";
CollectionReference request = FirebaseFirestore.instance
.collection('socails')
.doc("daaJgE8Pz5UQIlNh47UsmwWcqNi1")
.collection("requests");
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: request.doc("EobkN9fONF4IxmpErB1n").get(),
builder:
(BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasError) {
return const Text("Something went wrong");
}
if (snapshot.hasData && !snapshot.data!.exists) {
return const Text("Document does not exist");
}
if (snapshot.connectionState == ConnectionState.done) {
Map<String, dynamic> data =
snapshot.data!.data() as Map<String, dynamic>;
bool isRequested = data["isRequested"];
bool isApproved = data["isApproved"];
if (data["isRequested"] == true && data['isApproved'] == true) {
return GetSocialData();
}
// if (data['isApproved'] == false && data['isRequested'] == true) {
// return Column(
// children: [
// data['isApproved'] == false
// ? const CircularProgressIndicator()
// : GetSocialData()
// ],
// );
// }
if (data['isApproved'] == false && data["isRequested"] == false) {
return Center(
child: ElevatedButton(
onPressed: () {
SendRequest().updateUserData(
isApproved: false, isRequested: true);
setState(() {});
},
child: const Text("data send")));
} else {
return Column(children: [
CircularProgressIndicator(),
Text(snapshot.data!.data().toString())
]);
}
} else {
return const Text("Loading database");
}
});
// if (isRequested == true && isApproved == false) {
// return Center(
// child: ElevatedButton(
// onPressed: () {
// SendRequest()
// .updateUserData(isApproved: false, isRequested: true);
// },
// child: const Text("data send")));
// } else {
// return GetSocialData();
// }
}
}
i really don't know whats wrong since im new to flutter i dont know that much. if i were to use text widget to know if the value is changing i get to know that value isn't changing. this web app is connect to another android app and value of that bool is gonna be updated by that app
A flutter builder it is a one time read, because, if you want to use a realtime read, use a streambuilder, check that in documentation : Flutter Cloud Firestore.
FutureBuilder is used for one time response, like taking an image from Camera, getting data once from native platform (like fetching device battery), getting file reference, making an http request etc.
On the other hand, StreamBuilder is used for fetching some data more than once, like listening for location update, playing a music, stopwatch, etc.
In your case you should use StreamBuilder