Beginning in early 2020, the Xamarin team started collaborating with the Surface and Windows developer teams to think about a new dual screen device that we were about to launch at Microsoft, and which you have no doubt heard about by now, the Surface Duo. This new device, which also makes phone calls, poses some unique opportunities to create new, productive mobile experiences. Our engineering team leapt at the chance to showcase how powerful Xamarin development can be for Android and cross-platform developers alike. How do you display controls on one screen which then spans to another screen? Where do you put navigation? Should content flow below the hinge, or space evenly? So many questions…

Figure 1: Xamarin.Forms was featured at the Surface Duo SDK launch event.
Figure 1: Xamarin.Forms was featured at the Surface Duo SDK launch event.

At our Developer Day launch event in early February, we delivered a fully functional native application built with Xamarin.Forms. The most remarkable thing about this application is that no core modifications to Xamarin.Forms were needed in order to achieve this. It's a strong validation to the design of the product. You can explore the source at https://aka.ms/app-xamarintv.

During six years in the market, Xamarin.Forms has spanned six versions of iOS and Android, run on Windows Phones, tablets, and desktops from WPF to UWP and now WinUI, and even been extended by contributors to macOS, Linux, and Samsung's Tizen platform. Thousands of companies of all sizes are using Xamarin.Forms to power mobile and desktop apps used in the consumer market as well as suites of line-of-business needs.

For the Surface Duo, we added a TwoPaneView layout (based on work by our Windows team), and a whole bunch of new state triggers to help you adapt to new screen sizes, orientations, and postures. Xamarin.Forms 5 also introduces drag-and-drop gestures. What we've done in Xamarin.Forms 5 to make your experience developing for dual-screens more delightful is just the beginning of what you can do with this release. Xamarin.Forms 5 contains the simplicity and productivity we've added based on your constant feedback to make it easier and faster for you to deliver beautiful cross-platform applications that share more code than ever before.

To give you the whirlwind tour of the development power you'll experience using Xamarin.Forms 5, I'll run you through the Fly Me sample app I built in the hopes that one day soon I'll again be able to board a plane and travel the globe. Xamarin.Forms 5 uses Shell to simplify the top-level things that every app needs. It also adds new design features such as shapes and brushes; combined with control templates; these new features speed up your UI development.

Simplicity Is Primary

Simplicity starts at the container level of your application, commonly referred to as the app shell. In Xamarin.Forms 5, every app template starts with a simple Shell in a file named AppShell.xaml. This is where you describe the visible navigation structure for your application, whether using a flyout menu, tab menus, or any combination, as you can see in Figure 2.

Figure 2: Shell flyout menu
Figure 2: Shell flyout menu

Login and Flyout Navigation

Fly Me uses a flyout menu that flies out from the side of the UI over the app content. In Figure 2 you can see a header, flyout items to navigate to pages throughout the app, and a logout menu item. The code begins simply with the Shell container.

<Shell xmlns="..."
       xmlns:x="..."
       xmlns:views="clr-namespace:FlyMe.Views"
       xmlns:vm="clr-namespace:FlyMe.ViewModels"
       FlyoutHeaderTemplate="{DataTemplate views:HeaderView}"
       x:Class="FlyMe.AppShell">
       
       <Shell.BindingContext>
   <vm:AppViewModel />
       </Shell.BindingContext>
</Shell>

To begin populating the Shell, you add nodes for each item you wish to appear in the flyout. Each item can take a variety of properties including a named route for URI navigation, and the title and icon you wish to display. The content, of type ContentPage, that's displayed when the user selects the item, is contained in the ShellContent. The DataTemplate is used to make sure that the content is only created when you need it, thus keeping your application load time minimal.

<FlyoutItem Route="home" Title="My Flights" Icon="{StaticResource IconTabMyFlights}">
    <ShellContent ContentTemplate="{DataTemplate views:MyFlightsPage }" />
</FlyoutItem>
<FlyoutItem Title="Today" Icon="{StaticResource IconTabToday}">
    <ShellContent ContentTemplate="{DataTemplate views:TodayPage}" />
</FlyoutItem>

If all you have are a series of FlyoutItems, the application will load the first ShellContent and display the flyout menu icon in the upper left. In this application, you'll first display a log in page, and make sure that the rest of the application isn't accessible until the user authenticates. In order to do this, place a ShellItem before the FlyoutItems in the AppShell.xaml.

<ShellItem Route="login" IsVisible="{Binding IsNotLoggedIn}">
    <ShellContent ContentTemplate="{DataTemplate views:LoginPage}" />
</ShellItem>
<FlyoutItem Route="home" Title="My Flights" Icon="{StaticResource IconTabMyFlights}">
    <ShellContent ContentTemplate="{DataTemplate views:MyFlightsPage}" />
</FlyoutItem>

In XAML, the order of items matters, and you use that to your benefit here by making sure the ShellItem for the LoginPage appears before the FlyoutItem. This tells Shell to display just the first and not the second item. The same pattern is useful for displaying onboarding sequences and the like before the rest of your application is displayed. If you've used XAML before, this mixing of visual and non-visual elements may seem a bit odd. The AppShell is a unique use of XAML that both expresses the content of the application and some UI expectations (that you want Shell to render a flyout or tabs or both). A ContentPage, by comparison, explicitly describes UI elements.

If you've used XAML before, this mixing of visual and non-visual elements may seem a bit odd.

In order to hide the log in page and let Shell proceed with displaying the rest of the application, you use the new ShellItem.IsVisible property. Any item in your Shell that should not be navigable may be protected by setting that property to false.

Styling Your Flyout

You could create renderers to completely replace all the content of the flyout, but that tends to be a lot more code than you really need. Xamarin.Forms 5 simplifies adding content to the header, styling the flyout item templates, and even styling the backdrop that appears behind the flyout but over the page. You can instead add your own header content using the Shell's FlyoutHeaderTemplate property, which in this example, is simply a grid and an image:

<Grid RowDefinitions="66,120" BackgroundColor="#5561F9">
    <Image Grid.RowSpan="2" VerticalOptions="Center" HorizontalOptions="Center"
   Source="{FontImage FontFamily=FontAwesome, Glyph=&#xf1d8;, Color=GhostWhite, Size=32}"/>
</Grid>

Each FlyoutItem has a title and icon. When you wish to style them further, changing the font size, colors, and selected states, you can now use new style classes to achieve the look you desire. Three classes and two element names are now available, as shown in Table 1.

Using these style classes, you can style the layout for each flyout item and use VisualStateManager with the new TargetName property to change the color of the label based on selection, as seen in Listing 1.

Listing 1: Styling the layout of the flyout items

<Style TargetType="Layout" ApplyToDerivedTypes="True" Class="FlyoutItemLayoutStyle">
    <Setter Property="HeightRequest" Value="44" />
    <Setter TargetName="FlyoutItemLabel" Property="Label.FontSize" Value="16" />
    <Setter TargetName="FlyoutItemLabel" Property="Label.TextColor" Value="{StaticResource TextOnLightColor}" />
    <Setter TargetName="FlyoutItemLabel" Property="Label.HeightRequest" Value="44" />
    <Setter Property="VisualStateManager.VisualStateGroups">
<VisualStateGroupList>
    <VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
    <VisualState.Setters>
    </VisualState.Setters>
</VisualState>
<VisualState x:Name="Selected">
    <VisualState.Setters>
<Setter Property="BackgroundColor" Value="#FF3300" />
<Setter TargetName="FlyoutItemLabel" Property="Label.TextColor" Value="White" />
    </VisualState.Setters>
</VisualState>
    </VisualStateGroup>
</VisualStateGroupList>
    </Setter>
</Style>

Before I finish discussing Shell, one last styling challenge to conquer is changing the color and opacity of the flyout's backdrop. The backdrop is the layer that sits behind the flyout and in front of the content page. The new FlyoutBackdrop takes a color or brush and is applied to any ShellItem via an attached property.

<Style TargetType="ShellItem" ApplyToDerivedTypes="true">
    <Setter Property="Shell.FlyoutBackdrop" Value="#CC333333"/>
</Style>

Limitless Design

Xamarin.Forms 5 introduces several new design-focused capabilities, including embedded fonts, font image support, shapes and paths, and brushes. These enhancements combine to bring any UI design within easy reach by using simple APIs shipping “in the box.”

Embedded Fonts and Font Image Source

Adding fonts is now easier than ever, whether for beautifying your text or for using font icon glyphs. Add any mobile-supported font file such as TTF or OTF to your Xamarin.Forms .NET Standard Library and set the build type to “Embedded Resource.” You can then add an assembly tag that tells the build tasks to make that font available by name or alias to your app, no matter which platform it's running on.

Fly Me uses Font Awesome, a popular free font with useful icons. In any *.cs file in the project, you can add the assembly attribute ExportFont. Although you could use the filename to refer to the font, it's helpful to provide an alias to declare your preferred name.

[assembly: ExportFont("fa-solid-900.ttf", Alias = "FontAwesome")]

To then display icons using the embedded font, assign your FontImageSource to any control that accepts an image source. You need only provide the font family, the glyph to be displayed, and styling information such as color and size. The flyout header demonstrates this using the handy FontImage markup extension.

Shapes, Paths, and Clipping

With shapes and paths, you have a whole new way to achieve your designs in Xamarin.Forms. Paths are a series of vector points and lines that can describe complex shapes. Due to their complexity, you won't really want to type the path data by hand. From almost any design tool (I like Figma) you can copy path data for a vector shape and copy it right into your code. In Figure 3 you can see a different style of login screen that uses an interesting shape for the form background.

Figure 3: Log in with shapes, paths, and clipping
Figure 3: Log in with shapes, paths, and clipping
<Path Fill="#333333"
      Data="M251,0 C266.463973,-2.84068575e-15 279,12.536027 279,28 L279,276 C279,291.463973 266.463973,304 251,304 L214.607,304 L214.629319,304.009394 L202.570739,304.356889 C196.091582,304.5436 190.154631,308.020457 186.821897,313.579883 L186.821897,313.579883 L183.402481,319.283905 C177.100406,337.175023 160.04792,350 140,350 C119.890172,350 102.794306,337.095694 96.5412691,319.115947 L96.5273695,319.126964 L92.8752676,313.28194 C89.5084023,307.893423 83.6708508,304.544546 77.3197008,304.358047 L65.133,304 L28,304 C12.536027,304 1.8937905e-15,291.463973 0,276 L0,28 C-1.8937905e-15,12.536027 12.536027,2.84068575e-15 28,0 L251,0 Z"
/>

Be glad that you don't have to type that data string yourself, though with some practice you'll get pretty good understanding what's going on. SVG images also commonly use path data, and you can use your favorite text editor to read and copy the data string.

You can also draw primitive shapes like ellipse, line, polygon, polyline, and rectangle. Each of these shapes support common styling properties such as aspect, fill, and a variety of stroke options.

<StackLayout Orientation="Horizontal" HorizontalOptions="Center">
    <Ellipse Fill="#FF9900" />
    <Line />
    <Ellipse />
    <Line />
    <Ellipse />
</StackLayout>

One of the most powerful uses of shapes is the ability to clip other controls, also known as “masking.” A square profile image can become a circle by applying an EllipseGeometry to the Image.Clip property. The same can be done with a path or any other shape, and clipping can be applied to any control or layout in Xamarin.Forms!

<Image HorizontalOptions="Center" VerticalOptions="Center" WidthRequest="150" HeightRequest="150" Source="profile.png">
    <Image.Clip>
        <EllipseGeometry Center="75,75" RadiusX="75" RadiusY="75"/>
    </Image.Clip>
</Image>

Dark Mode

Modern operating systems all now have some form of support for light and dark modes, like those seen in Figure 4. These modes may be triggered by ambient light sensors, time of day, or by user preference. You can make your colors and styles aware of appearance modes using a new binding extension appropriately called AppThemeBinding. If you use the default colors provided by the platform, and you make no customization, and then your application uses the platform default colors for light and dark appearance modes. To take more creative control, update your application styles and set the colors directly.

Figure 4: Appearance modes support light and dark
Figure 4: Appearance modes support light and dark
<Style TargetType="Page" ApplyToDerivedTypes="True">
    <Setter Property="BackgroundColor" Value="{AppThemeBinding Dark=#222222, Light=#f1f1f1}" />
</Style>

Now when appearance mode changes on the devices for any reason, the application updates at runtime. How can you then opt-out of this behavior and allow the user to set their own appearance mode preference? Xamarin.Forms 5 provides App.Current.UserAppTheme that you can set from anywhere in the application. You can choose from OSAppTheme.Dark, OSAppTheme.Light, or OSAppTheme.Unspecified. Choosing Unspecified gives control back to the platform to trigger appearing changes.

App.Current.UserAppTheme = OSAppTheme.Dark;

New Control Customizations

Windows desktop platforms have long enjoyed “lookless controls” that allow you to skin parts of a control by supplying a control template. You may not realize this, but Xamarin.Forms has supported control templates since version 2! In Figure 5, you can see the default RadioButton control without any styling applied. As you would expect the XAML is plain.

Figure 5: Basic RadioButton controls
Figure 5: Basic RadioButton controls
<StackLayout RadioButtonGroup.GroupName="SimpleRadios" Orientation="Horizontal">
    <RadioButton Content="Day" />
    <RadioButton Content="Week" />
    <RadioButton Content="Month" />
</StackLayout>

The main difference between control templates in Xamarin.Forms versus other platforms is that, because the controls have adhered closely to the native experience, we didn't provide the ability to supply a template that works seamlessly with core controls...until now. In Xamarin.Forms 5, we're introducing support for control templating on the new RadioButton control.

We didn't provide the ability to supply a template that works seamlessly with core controls...until now

You can apply a control template, such as Listing 2, directly to the RadioButton.ControlTemplate property, or better yet by setting a style. And because RadioButton takes any content, you can provide layout and controls that will be applied to the ContentPresenter in the template. Check out how much better this looks now in Figure 6!

Listing 2: Control template for a RadioButton

<ControlTemplate x:Key="CalendarRadioTemplate">
    <Frame HasShadow="False" HeightRequest="100" WidthRequest="100" HorizontalOptions="Start" VerticalOptions="Start" Padding="0">
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroupList>
                <VisualStateGroup x:Name="CommonStates">
                    <VisualState x:Name="Normal">
                        <VisualState.Setters>
                            <Setter Property="BackgroundColor" Value="#f3f2f1"/>
                            <Setter Property="BorderColor" Value="DarkBlue"/>
                            <Setter Property="Grid.IsVisible" TargetName="RadioIcon" Value="False"/>
                        </VisualState.Setters>
                    </VisualState>
                    <VisualState x:Name="Checked">
                        <VisualState.Setters>
                            <Setter Property="BorderColor" Value="DarkBlue"/>
                            <Setter Property="Grid.IsVisible" TargetName="RadioIcon" Value="False"/>
                        </VisualState.Setters>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateGroupList>
        </VisualStateManager.VisualStateGroups>
        
        <Grid Margin="4" WidthRequest="100">
            <Grid x:Name="RadioIcon" WidthRequest="18" HeightRequest="18" HorizontalOptions="End" VerticalOptions="Start">
                <Ellipse Stroke="DarkBlue" WidthRequest="16" HeightRequest="16" StrokeThickness="0.5" VerticalOptions="Center" HorizontalOptions="Center" Fill="White" />
                <Ellipse WidthRequest="8" HeightRequest="8" Fill="DarkBlue" VerticalOptions="Center" HorizontalOptions="Center" />
            </Grid>
            <ContentPresenter></ContentPresenter>
        </Grid>
    </Frame>
</ControlTemplate>
<RadioButton ControlTemplate="{StaticResource CalendarRadioTemplate}">
    <RadioButton.Content>
<StackLayout HorizontalOptions="Center" VerticalOptions="Center">
    <Image Source="{FontImage FontFamily=FontAwesome, Glyph=&#xf783;, Color=#323130, Size=32}"/>
    <Label Text="Day" TextColor="#323130"/>
</StackLayout>
    </RadioButton.Content>
</RadioButton>
Figure 6: RadioButtons with content and control template
Figure 6: RadioButtons with content and control template

Although this is traditionally something you'd only think of doing in XAML, and I know that many of you do just that, control templates are equally powerful directly from C#. You just need to assign a custom template to the control's template property; described in C# or XAML, it doesn't matter.

Supplying a control template is a very powerful and convenient way to fully customize a control. When you do this, be aware that the control no longer uses the native platform control but only the cross-platform features. In most cases, this flexibility and control far outweighs that trade-off. For special cases, you can always adopt the custom renderer strategy.

What's Next

Xamarin.Forms 5 is a monumental release, and it wouldn't be that without amazing work from our many contributors. In this release, as compared to the last, contributions have increased nearly 34%. In 2020, Xamarin.Forms has twice set new high marks for usage, and you've reported to us the highest satisfaction ratings the platform has ever seen. This is really a celebration of you and your continued contributions and collaboration. Thank you!

Much of Xamarin.Forms 5 has been in preview for several months while we worked through your feedback to make it ready for a stable release. This article showcases only some the new features at your disposal. See the documentation at https://docs.microsoft.com/xamarin/whats-new/ for details about many more features, such as those shown in Table 2.

Our .NET team is already working on .NET 6 and the next release that will focus on surmounting significant challenges in app performance, improved desktop support, and advanced control customizations. It's encouraging that everything you invest today in .NET and Xamarin.Forms 5 has a future path for many years to come within .NET.

To get started today with Xamarin.Forms 5, you can quickly update your existing projects via your favorite NuGet package manager. The new project templates in Visual Studio will also be updated to use Xamarin.Forms 5 by default, so you get the very best experience. Continue to send us feedback and let us know how we're doing. Happy coding!

Table 1: The `available` classes and elements

Flyout Item Part Style Class Name Element Name
TextFlyoutItemLabelStyleFlyoutItemLabel
IconFlyoutItemIconStyleFlyoutItemIcon
ContainerFlyoutItemLayoutStyle

Table 2: There are many more features to explore.

In Xamarin.Forms 5 you'll also find:
Accessibility - TabIndexFontImageSourceRefreshView
AdaptiveTriggerGIF supportShapes & Paths
AndroidXGrid Row/Col simplificiationShell Modals
AppTheme (Dark Mode)HTML LabelSourceLink
Brushes - gradient and solidImage loading and error sourceSpanModeStateTrigger
Bug fixesIndicatorViewStateTrigger
CarouselViewKerning / Character SpacingSwipeView
CheckBoxLabel paddingSwitch VisualStates
ClippingLabel TransformTransparent background modals
CollectionViewMap ShapesTwoPaneView
CompareStateTriggerMultiBindingsVarious markup extensions
DeviceStateTriggerMultiTriggersVarious Platform Specifics
Drag & Drop gesturesNative enhancements to Maps and WebViewVisualStateManager Target
DualScreen SDKOrientationStateTriggerWebView Cookies