Conditional bottom navigation bar - flutter

I have bottom navigation bar which consist of 5 icon which will redirect to 5 screen correspondingly.
The fifth icon is Account screen so when no session it must redirect to Login screen and then load the Account screen. But after login when user tap the Account icon just redirect to Account screen.
How to achieve this? How can I use if condition to determine which layout to load? Error says missing identifier expexted ')'

You can easily achieve this with by passing an async function for the onPressed of Account icon. Your async function would look something like.
Example:
//On Home Page, for account icon pressed
Future<void> checkCredsAndNavigate(){
bool loggedIn = _checkIfLoggedIn(); // this might be a function that gets status of user login, you can fetch from prefs, state, etc.
if(!loggedIn){
var data = await Navigator.of(context).pushNamed('YOUR_LOGIN_ROUTE_NAME');
if(data == null){ // Check for data, will be null when user cancels login
return;
}
}
Navigator.of(context).pushNamed('YOUR_ACCOUNT_ROUTE_NAME');
}
//On Login page,
// On successful login call
Navigator.of(context).pop(data); // this data can be anything like user id, or just a boolean indicating successful login,etc.
//To cancel login, call
Navigator.of(context).pop();
Hope this helps!

Related

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'));

How to navigate to specific screen page based on the type of user logged in?

I'm having a mobile app with many screen (sreen A, screen B, screen C ...)
The requirement in my application to include two types of users. One type of user will have access to all screen and the second type user (not loggin) will only have access to Screen A , Screen B. How can I do that ?
My idea is store token after user loggin by SharedPreferences. And check the token is null or not. If not null, user can access all screen. But I don't know where to put this code ? At the main.dart or each screen ?
getToken() async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
String token = sharedPreferences.getString("token");
//print(token);
return token;
}
//if token != null { ..can access all Screen } else { ... }
Wrap your home screen in a Futurebuilder. use getToken() as the future for this future builder. Based on the data returned from your function, return the screen you want.
you have two options:
create a global.dart file and save getToken() and also a variable for the storedvalue of token (so only check for the value of sharedPref once) in there. then you can access it as follow:
import './globals.dart' as globals;
globals.accessToken // for getting token value
//or
globals.getToken() // for getting token directly from sharedPref
use state managements like provider

Flutter: conditional bottom navigation bar, to show pages based on if condition

I have implemented a bottom navigation bar with the logic listed below, and I want to check if the user is logged in. If user is logged in on third tab I want to show the profile page instead of SignIn page. This is my code where I am displaying the bottom navigation bar, I am stuck on what to do after checkIsLoggedIn() async function.
After that Im building list of widgets to show the pages in 3 tabs.
code of which is below,
so in place of SignIn(), I need to show, if the user is logged in it shows signin other wise, it shows Profile page.
Please do help me out here.
This may help.
Future<void> checkIfLoggedIn() async {
SharedPreferences localStorage = await SharedPreferences.getInstance();
var id = localStorage.getString('id');
if (id != null) {
setState(() {
_children[2] = ProfilePage();
});
}
}
Inside checkIfLoggedIn(), along with _isLoggedIn write the following code: _children[2] = Profile().
You can set the last BottomNavigationItem as below
BottomNavigationItem(
icon:_isLoggedIn ? Icons.person : Icons.key,
title:Text(_isLoggedIn ? 'Account' : 'Log in' )
)
and you can handle the click event and set the proper page in the build() by the same ternary operators using the _isLoggedIn variable
build(){
return Scaffold(
body:_isLoggedIn?ProfilePage():LoginPage()
);
}

Flutter Navigation showing route as /

I have a card with PopupMenuButton that I can tap to perform the 'copy' action. I can also perform the same action from the card details page. The problem I have is to get back to the home page I have to cater for both paths. So if the action is performed from the details page, I need to pop twice to go back to the home page.
Action from the home page:
Home > add card page
navigator.pop() takes me back to home page
Action from the details page:
home > card details page > add card page
Having to pop twice here.
I know I have to do the pop atleast once and then I used the popUntil to check the current state of the route. For some reason, it is always popping twice.
After debugging, its showing current route as /
void _changeRoute(String newRouteName) {
bool isNewRoute = false;
// Pop once
Navigator.pop(context); // after this I should be on home page for the first path
Navigator.popUntil(context, (route) {
print('Current route is ${route.settings.name}');
if (route.settings.name == newRouteName) { // but this is showing current route as /
isNewRoute = true;
}
return true;
});
print('is home page: $isNewRoute');
if (!isNewRoute) {
Navigator.pop(context);
}
}
I have this working.
My problem was that in my Main App, I had specified the home property on MaterialApp. According to the documentation, if the home property is specified, it takes the route /. So my home page is / and I had it defined as /home in my routes and this was being overridden by /.

How to properly implement Logout with firebase on 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
}
});