Search code examples
c#xamlxamarin.formsxamarin.forms.shell

Reset xamarin.forms.shell to initial page on back button


Very short version is I want to, in code, make the shell go to where it was, and how it looked, when it first displayed on app start.

I have a xamarin.forms.shell page which shows a number of content pages using flyout and tabs. When the shell initially loads, it shows the first flyout item content page, which is my "main activity/page" with the most common UI.

If I go to another flyout item content page, I would like the backbutton to navigate to the "main flyout" and only if back button is clicked on this "main flyout", should it leave the app.

While I can intercept the backbutton and navigate to other pages, it does not have the effect of clicking the "main page" flyout. If I do

Shell.Current.GoToAsync(name of route to my main flyout);

I do get to that main page, but as fullscreen and the flyout menu, toolbar and tabs are gone.


Solution

  • For this you need to override the Back button behavior, in your desired content page by defining a command that will be fired when the user pressed the back button in that page and then navigate to your root page by it defined root:

    CatsPage.Xaml

    <Shell.BackButtonBehavior>
       <BackButtonBehavior Command="{Binding BackCommand}"/>
    </Shell.BackButtonBehavior>
    

    Code-Behind or ViewModel

    public ICommand BackCommand { get; private set; }
    
    public CatsPage()  //or VM constructor
    {
        ...
    BackCommand = new Command(async (x) => await Shell.Current.GoToAsync("///MainRoute"));
    }
    

    If you are using this content page as a Tab content in more than one FlyoutItem, but you only want this behavior to work on the the ContentPage residing in the first FlyoutItem you may add a conditional test based on page absolute route or Title property:

    Xaml example from jiang answer:

    <FlyoutItem Route="MainRoute"
                Title="Animals"
                FlyoutDisplayOptions="AsMultipleItems">
        <Tab Title="MainPage"
             Route="MainPage"
             Icon="paw.png">
            <ShellContent Route="cats"
                          Style="{StaticResource DomesticShell}"
                          Title="Cats"
                          Icon="cat.png"
                          ContentTemplate="{DataTemplate views:CatsPage}" />
            <ShellContent Route="dogs"
                          Style="{StaticResource DomesticShell}"
                          Title="Dogs"
                          Icon="dog.png"
                          ContentTemplate="{DataTemplate views:DogsPage}" />
        </Tab>
    
        <ShellContent Route="PageTwo"
                      Style="{StaticResource MonkeysShell}"
                      Title="Monkeys"
                      Icon="monkey.png"
                      ContentTemplate="{DataTemplate views:MonkeysPage}" />
    
        <ShellContent Route="cats"
                      Style="{StaticResource DomesticShell}"
                      Title="Cats2"
                      Icon="cat.png"
                      ContentTemplate="{DataTemplate views:CatsPage}" />
    </FlyoutItem>
    
    

    Code-Behind or ViewModel

    public CatsPage() //or VM constructor
    {
        ...
        BackCommand = new Command(async (x) => {
        if (Shell.Current.CurrentState.Location.ToString().Equals("//MainRoute/MainPage/Cats"))
          //popup to quit
    else if (Shell.Current.CurrentState.Location.ToString().Equals("//MainRoute/Cats"))
            await Shell.Current.GoToAsync("///MainRoute");
        // or use to go back to previous: await Shell.Current.GoToAsync("..");
       });
    }
    

    Once again you need to properly define and use your Tabs and FlyoutItem routes hierarchy otherwise it will result in an exception.