How to Restrict/Control the Navigation Routes the User Can Visit Based on Login Status/Role

How can you restrict/control the navigation routes the user can visit based on login status/role?

Here is an example showing how you can control the visibility or navigation to your pages based on the login status of the user.

By default Shell will always displays initially the first element defined in AppShell.xaml, in this case it will be Login.xaml page.

In the below example "Page3" will be visible initially because by default (Isvisible=true), while "Page2" it will only be visible when the bindable property IsLogged is true.

  • You can handle any logic when user Log in/Log out in the IsLogged_PropertyChanged() event.

  • If you want several/specific or page based roles you can always create/define/design yours, use them in bindings, raise and use their property changed event to execute actions.



With FlyoutItem

AppShell.xaml

<Shell Shell.FlyoutBehavior="Disabled"..>
<FlyoutItem FlyoutDisplayOptions="AsMultipleItems">
<Tab>
<ShellContent Title="Login" Route="Login">
<local:Login />
</ShellContent>

<ShellContent Title="Page2" Route="Page2"
ContentTemplate="{DataTemplate local:Page2}"
IsVisible="{Binding IsLogged}"/>

<ShellContent Title="Page3" Route="Page3"
ContentTemplate="{DataTemplate local:Page3}"/>
</Tab>
</FlyoutItem>

AppShell.xaml.cs

public bool IsLogged
{
get => (bool)GetValue(IsLoggedProperty);
set => SetValue(IsLoggedProperty, value);
}

public static readonly BindableProperty IsLoggedProperty =
BindableProperty.Create("IsLogged", typeof(bool), typeof(AppShell), false, propertyChanged: IsLogged_PropertyChanged);

private static void IsLogged_PropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
//handle log in/log out event
if ((bool) newValue)
//user just logged in logic
else
//user just logged out logic
}

Login.xaml

<StackLayout>
<Label
FontSize="45"
HorizontalOptions="FillAndExpand"
Text="Login Page" />
<Button Clicked="Button_Clicked" Text="Log In" />
</StackLayout>

Login.xaml.cs

private async void Button_Clicked(object sender, System.EventArgs e)
{
IsVisible = false; //hide login page
//Trigger the binding to show pages previously hidden
(Shell.Current as AppShell).IsLogged = true;
await Shell.Current.GoToAsync("//Page2"); //navigate to main page (next after log)
//Enable the flyout: hamburger button
Shell.Current.FlyoutBehavior = FlyoutBehavior.Flyout;
}


Same thing with Tabs

In this example "Symbols" bottom tab won't be visible the same for upper tab "B" that belongs to bottom tab "Letters" until the user log-in, the remaining bottoms tabs will be visible initially.

AppShell.xaml

<TabBar>
<ShellContent Title="Login" Route="Login">
<local:Login />
</ShellContent>

<Tab Title="Letters">
<ShellContent
Title="A"
ContentTemplate="{DataTemplate local:Page1}"
Route="Page1" />

<ShellContent
Title="B"
ContentTemplate="{DataTemplate local:Page2}"
IsVisible="{Binding IsLogged}"
Route="Page2" />

<ShellContent
Title="C"
ContentTemplate="{DataTemplate local:Page3}"
Route="Page3" />
</Tab>

<Tab Title="Digits">
<ShellContent
Title="100"
ContentTemplate="{DataTemplate local:Page4}"
Route="Page4" />
</Tab>

<Tab Title="Symbols" IsVisible="{Binding IsLogged}">
<ShellContent
Title="!"
ContentTemplate="{DataTemplate local:Page5}"
Route="Page5" />
</Tab>
</TabBar>

Edit

You may also want to add Shell.NavBarIsVisible="False" and Shell.TabBarIsVisible="false" in the Tabbar case, to the LoginPage to respectively hide the navigation bar and hide the bottom tab bar.

xamarin navigation login management

There are two ways to achieve this .



Include LoginPage into AppShell

  1. Set AppShell as MainPage in App.

  2. Place Two Tabbar in AppShell , and place LoginPage first than HomePage, and set different Route for the two Tabbar.

    <TabBar Route="Login">
    <ShellContent ContentTemplate="{DataTemplate local:LoginPage}" />
    </TabBar>

    <TabBar Route="Home">
    <ShellContent Title="Menu" Icon="home.png" ContentTemplate="{DataTemplate local:AdminMenuPage}" />
    <ShellContent Title="Settings" Icon="settings.png" ContentTemplate="{DataTemplate local:SettingsPage}" />
    </TabBar>
  3. Call await Shell.Current.GoToAsync("//Home"); when login in , Call await Shell.Current.GoToAsync("//Login"); when login out .

Don't Include LoginPage into AppShell

  1. Set LoginPage as MainPage in App at first.
  2. Call MainPage = new AppShell(); When login in , Call MainPage = new LoginPage(); when login out .

Global routes currently cannot be the only page on the stack

You can set up a trick using the property FlyoutItemIsVisible, the ShellContent (thus the related page) will be registered in the Shell hierarchy but won't be visible in the flyout, also it will be loaded only when it is needed since we are using ContentTemplate.

Assuming you are only using the flyout without the bottom tabs:

AppShell.xaml

<Shell Shell.TabBarIsVisible="False"
...

<FlyoutItem>
<ShellContent ContentTemplate="{DataTemplate local:MainPage}"/>
<ShellContent Route="AnotherPage"
FlyoutItemIsVisible="False"
ContentTemplate="{DataTemplate local:AnotherPage}"/>

<ShellContent Route="LoginPage"
FlyoutItemIsVisible="False"
ContentTemplate="{DataTemplate local:LoginPage}"/>
</FlyoutItem>

<MenuItem Text="My Stats Page"
Clicked="MenuItem_Clicked"/>

AppShell.xaml.cs

async void MenuItem_Clicked(object sender, System.EventArgs e)
{
if (LoggedIn)
await Shell.Current.GoToAsync($"//{nameof(AnotherPage)}");
else
await Shell.Current.GoToAsync($"//{nameof(LoginPage)}");
}

Note

In the case you are using bottom tabs at the same time, or as another approach I suggest to hide unrelated pages to the current log status (example: hide LoginPage if the user is logged in).

How can you restrict/control the navigation routes the user can visit based on login status/role?

How to set the root of the navigation stack

This is a pure Android behavior and has nothing to do with Xamarin.Forms, when pressing the back button while your navigation stack of the app is empty, depending on which Android version is running, it will behave like follow:

  • Android 11 and lower: The system finishes the activity.
  • Android 12 and higher:

The system moves the activity and its task to the background instead of finishing the activity. This behavior matches the default system behavior when navigating out of an app using the Home button or gesture.

In most cases, this behavior means that users can more quickly resume your app from a warm state, instead of having to completely restart the app from a cold state...

Source: Back press behavior for root launcher activities.

In your case when you press the back button on the main screen, Android finishes the activity, if you want to confirm that, set a breakpoint on your AppShell.cs constructor or MainActivity.cs/OnCreate() you will notice that:

  • Home button pressed on main screen and restore back the app from android apps stack: none of the breakpoints will be hit because the app activity was conserved. In fact Android will call MainActivity.OnResume().
  • Back button press you will hit the breakpoints, because the activity was terminated and you are starting it over.

Some potential solutions

  1. Save and keep an updated record of the logging state on a local DB (SQLite) or a file, at the app startup read this bool and accordingly show or no the login page (set the mainpage).
  2. If you don't want your app to exit upon clicking back button, override OnBackPressed() in your MainActivity with an empty code:
public override void OnBackPressed()
{
}

  1. Send your app to the back (pause it) rather than terminate it:
   public override void OnBackPressed() => MoveTaskToBack(true);

More on OnBackPressed()

Related questions

  • What happens when back button or home or application removed from recent apps
  • How can you restrict/control the navigation routes the user can visit based on login status/role?

How to remove the navigation bar of a ContentPage when using Shell

Since you are using Shell, instead of setting the attached proeprty NavigationPage.HasNavigationBar you need to set Shell.NavBarIsVisible:

<ContentPage ...
Shell.NavBarIsVisible="False"

Related question

How can you restrict/control the navigation routes the user can visit based on login status/role?



Related Topics



Leave a reply



Submit