Login Flow in Xamarin.Forms Shell
If you create a new Xamarin.Forms Shell project based on the Flyout template, it's already going to include some boilerplate for the login process. To make it work for my use case, I had to make some improvements to it.
Remove login page from the flyout menu
Although not immediately obvious, the login page is by default still listed in the flyout menu just below the Logout menu item. You can't notice it because it doesn't have a label. But you can still click on it.
To fix that, you first need to upgrade the Xamarin.Forms NuGet package in your solution to version 4.8 or higher. This will add the IsVisible
property to the FlyoutItem
allowing you to completely remove the login page from the flyout menu. To do that, you need to wrap the existing ShellContent
element for LoginPage
with a FlyoutItem
element:
<FlyoutItem IsVisible="False">
<ShellContent Route="LoginPage"
Shell.FlyoutBehavior="Disabled"
ContentTemplate="{DataTemplate local:LoginPage}"/>
</FlyoutItem>
If you're like me, you might find the original comment above this line somewhat misleading. If you're wondering why the flyout menu is disabled when this page is displayed, it's because of the Shell.FlyoutBehavior="Disabled"
attribute.
Update: In the Flyout template in Visual Studio 2019 16.8, this issue is already fixed. It uses a different approach which unlike my fix above still works in Xamarin.Forms 5, so I suggest you always use the following markup instead:
<TabBar>
<ShellContent Route="LoginPage"
Shell.FlyoutBehavior="Disabled"
ContentTemplate="{DataTemplate local:LoginPage}"/>
</TabBar>
Force users to log in on application start
If your application requires the users to log in, you will need to check at the application startup whether the user has a valid login. Depending on the result, you will show him either the login page or the application home page:
I implemented the check in a simple page showing only an ActivityIndicator
:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ShellLogin.Views.StartupPage"
Shell.NavBarIsVisible="False">
<ContentPage.Content>
<StackLayout VerticalOptions="Center">
<ActivityIndicator IsRunning="True" />
</StackLayout>
</ContentPage.Content>
</ContentPage>
The Shell.NavBarIsVisible="False"
attribute hides the navigation bar at the top. The rest should be self-explanatory.
I added the page as the first (hidden) FlyoutItem
in the Shell and disabled the flyout menu for it:
<FlyoutItem IsVisible="False">
<ShellContent Route="StartupPage"
Shell.FlyoutBehavior="Disabled"
ContentTemplate="{DataTemplate local:StartupPage}" />
</FlyoutItem>
I implemented the check in the OnAppearing
virtual method of the page:
protected override void OnAppearing()
{
base.OnAppearing();
CheckLogin();
}
private async Task CheckLogin()
{
// should check for valid login instead
await Task.Delay(2000);
// only open Login page when no valid login
await Shell.Current.GoToAsync($"//{nameof(LoginPage)}");
}
In a real application, I would most likely make an API call and redirect to the correct page accordingly instead of always opening the login page. It's important to use the //
prefix in the Uri to create a new navigation stack for the page.
Open additional pages on the login navigation stack
You might need more than one page for the user's login flow. For example, you could also provide the account registration functionality:
As this page won't be a part of the Shell hierarchy, its route must be explicitly registered in the code (typically in the AppShell
class constructor):
Routing.RegisterRoute(nameof(RegisterPage), typeof(RegisterPage));
To navigate to it, use only the route name:
await Shell.Current.GoToAsync($"{nameof(RegisterPage)}");
Since the page is not included in the Shell hierarchy, the flyout menu can't be disabled there. Instead, the Shell.FlyoutBehavior="Disabled"
attribute must be set in the page itself:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ShellLogin.Views.RegisterPage"
Shell.FlyoutBehavior="Disabled">
<ContentPage.Content>
<!--- ... -->
</ContentPage.Content>
</ContentPage>
When navigating away from this page, it's important to reset the navigation stack to the root page so that it will be ready when navigating back to it after a potential logout:
await Shell.Current.Navigation.PopToRootAsync(animated: false);
await Shell.Current.GoToAsync($"//{nameof(AboutPage)}");
A sample application with the complete login flow implemented is available in my GitHub repository.
The login page in the flyout template for Xamarin.Forms Shell project is a good starting point which can be further extended without too much work. The fact that the login page is accessible from the flyout menu is its only real problem. To fix it, the Xamarin.Forms NuGet package must be updated. With some knowledge about the Shell behavior additional pages can be added to the application:
- to be shown during the application initialization, or
- to be included in the login flow navigation stack.