All skills
davidortinau avatar

/maui-shell-navigation

@73e29ff

.NET MAUI Shell navigation guidance — Shell visual hierarchy, AppShell setup, tab bars, flyout menus, URI-based navigation with GoToAsync, route registration, query parameters, back navigation, and navigation events. USE FOR: "Shell navigation", "GoToAsync", "AppShell", "tab bar", "flyout menu", "route registration", "query parameters navigation", "back navigation", "Shell tabs", "URI navigation", "navigation events". DO NOT USE FOR: deep linking from external URLs (use maui-deep-linking), data binding on pages (use maui-data-binding), or dependency injection setup (use maui-dependency-injection).

Use this Skill: https://skilld.dev/gh/davidortinau/maui-skills/maui-shell-navigation

This session only. Nothing lands on disk.

referencesshell-navigation-api.md

≈2.3k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Shell Navigation API Reference

Shell Visual Hierarchy

Shell uses a four-level hierarchy. Each level wraps the one below it:

Shell
 ├── FlyoutItem / TabBar          (top-level navigation grouping)
 │    ├── Tab                     (bottom-tab grouping)
 │    │    ├── ShellContent        (page slot; points to a ContentPage)
 │    │    └── ShellContent        (creates top tabs within a bottom tab)
 │    └── Tab
 └── FlyoutItem / TabBar
  • FlyoutItem – appears in the flyout menu. Contains one or more Tab children.
  • TabBar – bottom tab bar with no flyout entry. Use when the app has no flyout.
  • Tab – groups ShellContent objects. Multiple ShellContent in one Tab produces top tabs.
  • ShellContent – each represents a ContentPage.

Implicit Conversion

You can omit intermediate wrappers. Shell auto-wraps:

You write Shell creates
ShellContent only FlyoutItem > Tab > ShellContent
Tab only FlyoutItem > Tab
ShellContent in TabBar TabBar > Tab > ShellContent

This keeps simple apps concise while allowing full control when needed.

AppShell.xaml Setup

<Shell xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
       xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
       xmlns:views="clr-namespace:MyApp.Views"
       x:Class="MyApp.AppShell"
       FlyoutBehavior="Flyout">

    <FlyoutItem Title="Animals" Icon="animals.png">
        <Tab Title="Cats">
            <ShellContent Title="Domestic"
                          ContentTemplate="{DataTemplate views:DomesticCatsPage}" />
            <ShellContent Title="Wild"
                          ContentTemplate="{DataTemplate views:WildCatsPage}" />
        </Tab>
        <Tab Title="Dogs" Icon="dogs.png">
            <ShellContent ContentTemplate="{DataTemplate views:DogsPage}" />
        </Tab>
    </FlyoutItem>

    <TabBar>
        <ShellContent Title="Home" Icon="home.png"
                      ContentTemplate="{DataTemplate views:HomePage}" />
        <ShellContent Title="Settings" Icon="settings.png"
                      ContentTemplate="{DataTemplate views:SettingsPage}" />
    </TabBar>
</Shell>

Tab Configuration

Bottom Tabs

Multiple ShellContent (or Tab) children inside a TabBar or FlyoutItem produce bottom tabs.

Top Tabs

Multiple ShellContent children inside a single Tab produce top tabs within that bottom tab:

<Tab Title="Photos">
    <ShellContent Title="Recent"  ContentTemplate="{DataTemplate views:RecentPage}" />
    <ShellContent Title="Favorites" ContentTemplate="{DataTemplate views:FavoritesPage}" />
</Tab>

TabBar Appearance (Attached Properties)

Set these on any page or Shell element:

Attached Property Type Purpose
Shell.TabBarBackgroundColor Color Tab bar background
Shell.TabBarForegroundColor Color Foreground / selected icon color
Shell.TabBarTitleColor Color Selected tab title color
Shell.TabBarUnselectedColor Color Unselected tab icon/title color
Shell.TabBarDisabledColor Color Disabled tab color
Shell.TabBarIsVisible bool Show/hide the tab bar
<ContentPage Shell.TabBarIsVisible="False" ... />

Flyout Configuration

FlyoutBehavior

Set on Shell:

<Shell FlyoutBehavior="Flyout"> ... </Shell>

Values: Disabled, Flyout, Locked.

FlyoutDisplayOptions

Controls how a FlyoutItem's children appear in the flyout:

<FlyoutItem Title="Animals" FlyoutDisplayOptions="AsMultipleItems">
    <Tab Title="Cats" ... />
    <Tab Title="Dogs" ... />
</FlyoutItem>
  • AsSingleItem (default) – one flyout entry for the group.
  • AsMultipleItems – each child Tab gets its own flyout entry.

Flyout Item Template

Customize appearance with Shell.ItemTemplate. BindingContext exposes Title and FlyoutIcon (FlyoutItem) or Text and IconImageSource (MenuItem):

<Shell.ItemTemplate>
    <DataTemplate>
        <Grid ColumnDefinitions="Auto,*" Padding="10">
            <Image Source="{Binding FlyoutIcon}" HeightRequest="24" />
            <Label Grid.Column="1" Text="{Binding Title}" VerticalTextAlignment="Center" />
        </Grid>
    </DataTemplate>
</Shell.ItemTemplate>

Replacing Flyout Content

<Shell.FlyoutContent>
    <CollectionView BindingContext="{x:Reference shell}"
                    ItemsSource="{Binding FlyoutItems}" />
</Shell.FlyoutContent>

MenuItem (non-navigation flyout entries)

<MenuItem Text="Log Out"
          Command="{Binding LogOutCommand}"
          IconImageSource="logout.png" />

Route Registration

Shell visual hierarchy items have implicit routes derived from their Route property (or type name). Detail pages not in the hierarchy must be registered:

// In AppShell constructor or MauiProgram
Routing.RegisterRoute("animaldetails", typeof(AnimalDetailsPage));
Routing.RegisterRoute("editanimal", typeof(EditAnimalPage));

Navigation with GoToAsync

All programmatic navigation goes through Shell.Current.GoToAsync:

// Absolute – navigate to a specific place in the hierarchy
await Shell.Current.GoToAsync("//animals/cats/domestic");

// Relative – push a registered page onto the navigation stack
await Shell.Current.GoToAsync("animaldetails");

// With query string
await Shell.Current.GoToAsync($"animaldetails?id={animal.Id}");

Absolute vs Relative Routes

Prefix Meaning
// Absolute route from Shell root
(none) Relative; pushes onto the current nav stack
.. Go back one level in the navigation stack
../ Go back then navigate forward
// Go back one page
await Shell.Current.GoToAsync("..");

// Go back two pages
await Shell.Current.GoToAsync("../..");

// Go back one page, then navigate to edit
await Shell.Current.GoToAsync("../editanimal");

Query Parameters

QueryProperty Attribute

[QueryProperty(nameof(AnimalId), "id")]
public partial class AnimalDetailsPage : ContentPage
{
    public string AnimalId { get; set; }
}

// Navigate with query string:
await Shell.Current.GoToAsync($"animaldetails?id={animal.Id}");

IQueryAttributable Interface

Preferred for ViewModels — gives you all parameters in one call:

public class AnimalDetailsViewModel : ObservableObject, IQueryAttributable
{
    public void ApplyQueryAttributes(IDictionary<string, object> query)
    {
        if (query.TryGetValue("id", out var id))
            AnimalId = id.ToString();
    }
}

The interface works on the page itself or on any object set as the page's BindingContext.

Passing Complex Objects

Use ShellNavigationQueryParameters (dictionary of string → object) to pass objects without serializing to strings:

var parameters = new ShellNavigationQueryParameters
{
    { "animal", selectedAnimal }  // pass the object directly
};
await Shell.Current.GoToAsync("animaldetails", parameters);

Receive via IQueryAttributable:

public void ApplyQueryAttributes(IDictionary<string, object> query)
{
    Animal = query["animal"] as Animal;
}

Navigation Events

Override in your AppShell:

protected override void OnNavigating(ShellNavigatingEventArgs args)
{
    base.OnNavigating(args);
    if (hasUnsavedChanges && args.Source == ShellNavigationSource.Pop)
        args.Cancel();  // prevent leaving
}

protected override void OnNavigated(ShellNavigatedEventArgs args)
{
    base.OnNavigated(args);
    // args.Current, args.Previous, args.Source
}

For async checks, use args.GetDeferral() → do work → deferral.Complete().

ShellNavigationSource values: Push, Pop, PopToRoot, Insert, Remove, ShellItemChanged, ShellSectionChanged, ShellContentChanged, Unknown.

Inspecting Navigation State

// Current URI location
ShellNavigationState state = Shell.Current.CurrentState;
string location = state.Location.ToString();  // e.g. "//animals/cats/domestic"

// Current page
Page page = Shell.Current.CurrentPage;

// Navigation stack of the current tab
IReadOnlyList<Page> stack = Shell.Current.Navigation.NavigationStack;

Back Button Behavior

Customize the back button per page:

<Shell.BackButtonBehavior>
    <BackButtonBehavior Command="{Binding BackCommand}"
                       IconOverride="back_arrow.png"
                       TextOverride="Cancel" />
</Shell.BackButtonBehavior>

Properties: Command, CommandParameter, IconOverride, TextOverride, IsVisible, IsEnabled.

Source: SKILL.md on GitHub

No alerts15d4 checks · Risk SAFE
  • Gen Agent Trust Hub15d

    The skill provides legitimate guidance and API references for .NET MAUI Shell navigation. No security risks were identified.

  • Socket15d

    No alerts

  • Snyk15d

    Risk: LOW · No issues

  • Runlayer7mo

    1 file scanned · No issues

Signed by skilld at 73e29ff. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 months ago.

Steadyupdated 6 months ago

README badge

README badge for davidortinau/maui-skills/maui-shell-navigation