This page describes how create a Shell in MauiReactor
.NET Multi-platform App UI (.NET MAUI) Shell reduces the complexity of app development by providing the fundamental features that most apps require, including:
A single place to describe the visual hierarchy of an app.
A common navigation user experience.
A URI-based navigation scheme that permits navigation to any page in the app.
.NET Multi-platform App UI (.NET MAUI) Shell includes a URI-based navigation experience that uses routes to navigate to any page in the app, without having to follow a set navigation hierarchy. In addition, it also provides the ability to navigate backwards without having to visit all of the pages on the navigation stack.
MauiReactor allows the registration of components with routes just like you do with Page in normal Maui applications.
To register a route you have to use the Routing.RegisterRoute<Component>("page name") method.
The following example shows how to register a few routes and how to navigate to them:
Shell navigation in action
Passing arguments to pages (components) would mean creating a Props class for the target page and using an overload of the GotoToAsync as shown below:
Passing arguments to pages
If you want to pass arguments to a modal page, use the overload of the Navigation.PushModalAsync<Page, Props>() method.
Shell TitleView
Shell control enables any View to be displayed in the navigation bar using the TitleView. In MauiReactor, you can set up a custom view using the TitleView fluent method of the ContentPage.
ContentPage must be installed inside a Shell to enable the TitleView
You can also customize the Window title with custom content. For more info, refer to the Window control page.
public override VisualNode Render()
=> new Shell
{
new FlyoutItem
{
new Tab
{
new ShellContent("Home")
.Icon("home.png")
.RenderContent(()=> new HomePage()),
new ShellContent("Comments")
.Icon("comments.png")
.RenderContent(()=> new CommentsPage()),
}
.Title("Notifications")
.Icon("bell.png"),
new ShellContent("Home")
.Icon("database.png")
.RenderContent(()=> new DatabasePage()),
new ShellContent("Comments")
.Icon("bell.png")
.RenderContent(()=> new NotificationsPage()),
}
.FlyoutDisplayOptions(MauiControls.FlyoutDisplayOptions.AsMultipleItems)
};
public override VisualNode Render()
=> new Shell
{
new ShellContent("Home")
.Icon("home.png")
.RenderContent(()=> new HomePage()),
new ShellContent("Comments")
.Icon("comments.png")
.RenderContent(()=> new CommentsPage()),
}
.ItemTemplate(RenderItemTemplate);
static VisualNode RenderItemTemplate(MauiControls.BaseShellItem item)
=> new Grid("68", "Auto, *")
{
new Image()
.Source(item.FlyoutIcon)
.Margin(4),
new Label(item.Title)
.GridColumn(1)
.VCenter()
.TextDecorations(TextDecorations.Underline)
.FontAttributes(MauiControls.FontAttributes.Bold)
.Margin(10,0)
};
public override VisualNode Render()
=> new Shell
{
new ShellContent("Home")
.Route(nameof(HomePage))
.Icon("home.png")
.RenderContent(()=> new HomePage()),
new ShellContent("Comments")
.Route(nameof(CommentsPage))
.Icon("comments.png")
.RenderContent(()=> new CommentsPage()),
}
.FlyoutContent(RenderFlyoutContent());
VisualNode RenderFlyoutContent()
{
return new ScrollView
{
new VStack(spacing: 5)
{
new Button("Home")
.OnClicked(async ()=> await MauiControls.Shell.Current.GoToAsync($"//{nameof(HomePage)}")),
new Button("Comments")
.OnClicked(async ()=> await MauiControls.Shell.Current.GoToAsync($"//{nameof(CommentsPage)}")),
}
};
}
public override VisualNode Render()
=> new Shell
{
new ShellContent("Home")
.Route(nameof(HomePage))
.Icon("home.png")
.RenderContent(()=> new HomePage()),
new ShellContent("Comments")
.Route(nameof(CommentsPage))
.Icon("comments.png")
.RenderContent(()=> new CommentsPage()),
new MenuItem("Click me!")
.OnClicked(async ()=> await ContainerPage.DisplayAlert("MauiReactor", "Clicked!", "OK"))
};
public override VisualNode Render()
=> new Shell
{
new MenuItem("Click me!")
.IconImageSource("gear.png")
.OnClicked(async ()=> await ContainerPage.DisplayAlert("MauiReactor", "Clicked!", "OK"))
}
.MenuItemTemplate(menuItem =>
new Grid("65", "Auto, *")
{
new Image()
.Source(menuItem.IconImageSource)
.VCenter(),
new Label(menuItem.Text)
.TextColor(Colors.Red)
.VCenter()
.Margin(10,0)
.FontAttributes(MauiControls.FontAttributes.Bold)
.GridColumn(1)
}
.Padding(10,0)
);
public override VisualNode Render()
=> new Shell
{
new ShellContent("Home")
.Route(nameof(HomePage))
.Icon("home.png")
.RenderContent(()=> new HomePage()),
new ShellContent("Comments")
.Route(nameof(CommentsPage))
.Icon("comments.png")
.RenderContent(()=> new CommentsPage()),
}
.FlyoutBackground(new LinearGradient(45.0, new Color(255, 175, 189), new Color(100, 216, 243)))
.FlyoutHeader(RenderHeader())
.FlyoutFooter(RenderFooter())
;
private VisualNode RenderHeader()
{
return new VStack(spacing: 5)
{
new Label("MauiReactor")
.TextColor(Colors.White)
.FontSize(24)
.HorizontalTextAlignment(TextAlignment.Center)
.FontAttributes(MauiControls.FontAttributes.Bold)
};
}
private VisualNode RenderFooter()
{
return new Image("dotnet_bot.png");
}
public override VisualNode Render()
=> new Shell
{
new TabBar
{
new ShellContent("Home")
.Icon("home.png")
.RenderContent(()=> new HomePage()),
new Tab("Engage")
{
new ShellContent("Notifications")
.RenderContent(()=> new NotificationsPage()),
new ShellContent("Comments")
.RenderContent(()=> new CommentsPage()),
}
.Icon("comments.png")
}
};
private MauiControls.ShellContent _notificationsPage;
public override VisualNode Render()
=> new Shell
{
new TabBar
{
new ShellContent("Home")
.Icon("home.png")
.RenderContent(()=> new HomePage()),
new Tab("Engage")
{
new ShellContent(pageRef => _notificationsPage = pageRef)
.Title("Notifications")
.RenderContent(()=> new NotificationsPage()),
new ShellContent("Comments")
{
new ContentPage
{
new Button("Go to notifications")
.VCenter()
.HCenter()
.OnClicked(()=> MauiControls.Shell.Current.CurrentItem = _notificationsPage)
}
}
}
.Icon("comments.png")
}
.Set(MauiControls.Shell.TabBarBackgroundColorProperty, Colors.Aquamarine)
};
class MainPage : Component
{
protected override void OnMounted()
{
Routing.RegisterRoute<Page2>(nameof(Page2));
Routing.RegisterRoute<Page3>(nameof(Page3));
base.OnMounted();
}
public override VisualNode Render()
=> new Shell
{
new Page1()
};
}
class Page1 : Component
{
public override VisualNode Render()
{
return new ContentPage("Page1")
{
new VStack
{
new Button("Goto Page2")
.OnClicked(async ()=> await MauiControls.Shell.Current.GoToAsync(nameof(Page2)))
}
.HCenter()
.VCenter()
};
}
}
class Page2 : Component
{
public override VisualNode Render()
{
return new ContentPage("Page2")
{
new VStack
{
new Button("Goto Page3")
.OnClicked(async ()=> await MauiControls.Shell.Current.GoToAsync(nameof(Page3)))
}
.HCenter()
.VCenter()
};
}
}
class Page3 : Component
{
public override VisualNode Render()
{
return new ContentPage("Page3")
{
new VStack
{
new Button("Open ModalPage")
.OnClicked(async () => await Navigation.PushModalAsync<ModalPage>())
}
.HCenter()
.VCenter()
};
}
}
class ModalPage : Component
{
public override VisualNode Render()
{
return new ContentPage("Modal Page")
{
new VStack
{
new Button("Back")
.OnClicked(async () => await Navigation.PopModalAsync())
}
.HCenter()
.VCenter()
};
}
}
class Page2 : Component
{
public override VisualNode Render()
{
return new ContentPage("Page2")
{
new VStack
{
new Button("Goto Page3")
.OnClicked(async ()=> await MauiControls.Shell.Current.GoToAsync<PageWithArgumentsProps>(nameof(PageWithArguments), props => props.ParameterPassed = "Hello from Page2!"))
}
.HCenter()
.VCenter()
};
}
}
class PageWithArgumentsState
{ }
class PageWithArgumentsProps
{
public string ParameterPassed { get; set; }
}
class PageWithArguments : Component<PageWithArgumentsState, PageWithArgumentsProps>
{
public override VisualNode Render()
{
return new ContentPage("PageWithArguments")
{
new VStack(spacing: 10)
{
new Label($"Parameter: {Props.ParameterPassed}")
.HCenter(),
new Button("Open ModalPage")
.OnClicked(async () => await Navigation.PushModalAsync<ModalPage>())
}
//.HCenter()
.VCenter()
};
}
}