How to properly implement Logout with firebase on flutter - flutter

It sounds stupid but I cant really properly logout of my app and that is because I use multiple FirebaseAnimatedList and route on my MaterialApp
routes: <String,WidgetBuilder>{
'/StartAppPage':(BuildContext context)=>new StartAppPage(),
'/LoginPage':(BuildContext context)=> new LoginPage(),
'/HomePage':(BuildContext context)=> new HomePage)
},
So the app checks for use and routes to HomePage or Login based on is there is a user or not.
My Home page has a FirebaseAnimatedList and
on my Home page there is a Logout button that do this
await googleSignIn.signOut();
await FirebaseAuth.instance.signOut();
await FirebaseDatabase.instance.goOffline();
return Navigator.pushReplacementNamed(context, '/StartApp');
to HomePage and logout the user.
But when the other user login again the List shows data of the old user and the list is usable messing my Database
How can I properly implement this and the setPersistance is off or on makes no difference
Solution: Keep a single instance of your firebase user through the app. I recommend a global variable

After signing out, what you can consider doing here is check on the current screen if the user is authenticated or not. If the user is still logged-in, display the data. If not, then navigate to a Login screen.
FirebaseAuth.instance
.idTokenChanges()
.listen((User? user) {
if (user == null) {
debugPrint('User is currently signed out!');
// TODO: navigate to Login screen?
} else {
debugPrint('User is signed in!');
// TODO: display data
}
});

Related

Rebuild app or delete cache when redirect to home page Flutter

I have an app where when I logout with Firebase Auth, user's datas are still there, and I need to Hot restart the app to totally remove them.
I didn’t find a way to improve the signout method, so I want to try to rebuild app when user logout, or maybe delete the cache so user’s datas will no longer exist in app.
I have done some research but I can’t find any solution that can help me.
EDIT : Here's how I logout
Future<void> signOut() async {
await FirebaseAuth.instance
.signOut()
.then((value) => print('Sign Out'))
.onError((error, stackTrace) => print('Error in signed out'));
}
IconButton(
onPressed: () async {
await signOut();
Navigator.of(context, rootNavigator: true)
.pushAndRemoveUntil(
MaterialPageRoute(
builder: (BuildContext context) {
return const OnBoardingPage();
},
),
(_) => false,
);
},
icon: const Icon(Icons.logout))
I know that user's data are still there because I can display user.email in my onBoardingPage (where technically no data can be there because I logout previously).
Here a preview of my onBoardingPage :
After that, if I want to connect with another account, I will be connected to the previous user's session. The same if I want to create a new account, all new user's data will be into the previous connected user. To fix this problem, in development mode, I need to hot restart the app.
It seems like the user is not totally logout.

Why the firebase auth StreamBuilder navigate to the HomePage only when an existing user login, but doesn't navigate when a new user sign up in the app

I have the following:
`
StreamBuilder<User?>(
stream: FirebaseAuth.instance.idTokenChanges(),
builder: (context, AsyncSnapshot<User?> snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
final User? user = snapshot.data;
if (user == null) {
return Login();
}
return HomePage();
}
return Login();
},
);
**Problem:** when try to login with an existing user email and password, the stream builder move me to theHomePagenormall as expected, but when I try to sign up with a new user email and password, it created fine in the firebase console, but the screen doesn't change to theHomePage`.
when I debug to what happens, after I sign up, the ``useris not null as you see here:
but it just don't open the HomePage.
any ideas, or I'm missing something ?
I'm expecting that if the user login or sign up, the HomePage should opens.
and I want to know why the login works fine, but signing up just don't do nothing
Where did you print "user is not null as you see here:"?
Are you sure the Stream event gets triggered?
If not after user registered try to add this line and see if Stream gets triggered!
await user.reload();

Flutter - re-run previous page code after execution returns to it

I'm trying to figure out the best way to implement the proper navigation flow of a flutter app I'm building that involves a 3rd party authentication page (Azure AD B2C). Currently I have a page that serves simply as a "navigate to 3rd party auth login" page which is set as the initialRoute for my app. The first time through, it runs exactly the way I want it to, but I'm not able to figure out how to get that 'navigate to auth' page to re-run when navigated back to (after logout) so that the user ends up back at the 3rd party auth login page.
Basically what I'd like to do is, on logout - have the app navigate back to that page specified as the initialRoute page, and upon that page being navigated back to, have it re-launch the 3rd party auth login page just like it did the first time it executed.
I tried just awaiting the call to Navigator.push() and then calling setState((){}) afterwards, and that does re-display the page, but it just leaves that initial page sitting there, and doesn't end up triggering the execution the way it did the first time. initState() does not fire again, so neither does any of my code that's in there.
I've tried various methods off the Navigator object trying to reload the page or navigate to itself again, or just calling goToLogin() again after the await Navigator.push() call, nothing works.
Here's what I'm currently doing :
User launches the app, the initialRoute is LoginRedirect
class LoginRedirect extends StatefulWidget {
#override
_LoginRedirectState createState() => _LoginRedirectState();
}
class _LoginRedirectState extends State<LoginRedirect> {
#override
void initState() {
Utility.getConfig().then((value) {
config = value;
oauth = AadOAuth(config);
goToLogin(context);
});
super.initState();
}
void goToLogin(BuildContext context) async {
setState(() {
loading = true;
});
try {
await oauth.login(); // this launches the 3rd party auth screen which returns here after user signs in
String accessToken = await oauth.getAccessToken();
navigateToDashboard();
setState(() {
loading = false;
});
} on Exception catch (error) {
setState(() {
loading = false;
});
}
}
void navigateToDashboard() {
await navigator.push(context, MaterialPageRoute(builder: (BuildContext context) => Dashboard()));
// right here is where I'd like to call goToLogin() again after I Navigator.popUntil() back to this
// page, but if I try that I get an error page about how 'The specified child already
// has a parent. You must call removeView() on the child's parent first., java.lang
// .IllegalStateException and something about the bottom overflowed by 1063 pixels
}
}
After getting some config values and calling oauth.login() then I call a navigateToDashboard() method that pushes the Dashboard page on to the navigation stack.
Elsewhere in the code I have a logout button that ends up calling this code:
oauth.logout();
Navigator.popUntil(context, ModalRoute.withName('/LoginRedirect'));
which returns execution to where I called await Navigator.push() previously. But I can't figure out what I need to do there to have that LoginRedirect page execute again. I can't call goToLogin() again or it errors/crashes. I can't call initState() again, calling setState() doesn't do anything. I'm kinda stumped here, I thought this would be easy.
When logging out try: Navigator.pushReplacementNamed(context, "/LoginRedirect"); instead of Navigator.popUntil(context, ModalRoute.withName('/LoginRedirect'));

Firebase Authorization is not checking if the user is logged in properly

in my flutter app I want to check if a user is logged in before or not and based on this navigate them to either HomePage or SignIn page. The code I am using is not working fine, it is not navigating to the SignIn page after I've done registration and deleted the account in Firebase Console. In short, when I delete a user, who registered well before, in Firebase Console the application is keeping to show the HomePage and all the posts the user made. How can I handle it?
"In short, I want to navigate the user to SignIn page after I delete his account in Firebase Console"
Here is the code in my main.dart file:
_check(){
final FirebaseAuth auth = FirebaseAuth.instance;
final User? user = auth.currentUser;
if(user == null){
HiveDB.deleteUser();
return SignInPage();
}
HiveDB.putUser(id: user.uid);
return HomePage();
}
#override
void initState() {
// TODO: implement initState
super.initState();
_check();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: _check(),
routes: {
SignInPage.id:(context) => SignInPage(),
SignUpPage.id:(context) => SignUpPage(),
HomePage.id:(context) => HomePage(),
},
);
}
The _check() function is not working as it's desired...
While Firebase persists the user info in local storage and automatically restores it when the app/page reloads, this requires that it makes an asynchronous call to the server and thus may not be ready by the time your check runs.
Instead of depending on currentUser, you'll want to listen for changes to the authentication state through the authStateChanges stream. Most apps wrap this in a StreamBuilder and then show the correct screen based on the stream state and last event.
This is something that can be resolved by using async . the problem is you are getting response in future but the action is performed before. Please check the following documentation for detailed overview:
https://dart.dev/codelabs/async-await

How can I implement Sign Up/Login logic with Provider. Flutter, Dart, Mobile Development

Let' say I have three files: main.dart, LoginScreen.dart, HomePageScreen.dart. I am utilizing Provider architecture for state management and wrapping MaterialApp.
When the app startup, the main.dart file checks whether the user is already Logged in (FirebaseAuth.currentUser to check this). If it is logged in, navigate to HomePageScreen, if not then navigate to SignUpScreen. Something like this on code:
// Inside of main
AppUser user = await database.getCurrentUser(); // user == null if no fbAuth.currentUser
runApp(MultiProvider(
providers: [,
ChangeNotifierProvider(create: (context) => user),
// some other notifier providers,
],
child: MyApp(),
));
// Inside of MyApp
#override
Widget build(BuildContext context) {
return MaterialApp(
...
home: user == null ? SignupScreen() : HomePageScreen();
);
}
This is great, it is working as I expected. If currentUser is null, it navigates to SignupScreen, if not then to HomePageScreen.
Problem:
In HomepageScreen, it listens to the User provider and updates UI components accordingly. Therefore, when the user successfully signed up for a new account or log in, I want to change/update the content of the user in the provider, so that when the app screen shift from SignupScreen to HomePageScreen, it displays the correct information on HomePageScreen. However, I cannot find a way to change the reference of the user inside of the provider.
Things that I tried:
In AppUser.dart, I created the method for changing the user reference:
class AppUser with ChangeNotifier {
String username, email, password;
void changeReference(AppUser newlyCreatedUser) {
this.username = newlyCreatedUser.username;
this.password = newlyCreatedUser.password;
this.email = newlyCreatedUser.email;
}
}
This is really just changing the content of the user. This way I can update the user to a newly created user. However, my instinct tells me that this is a very bad way of solving this problem. I feel like the problem is laying in the logic, the way I am trying to implement the sign up/login feature.
Summary:
Is there a way to change the reference of the value in the provider?
If this is not possible, or a bad way of using provider architecture, do you have any suggestion of how to implement Sign up feature