First Steps on MAUI Beach

First Steps on MAUI Beach

ยท

10 min read

With the release of Visual Studio 2022 and .NET 6 in early November I decided it was time to set sail for .NET MAUI. This article documents my experiences starting a new cross platform app using .NET MAUI Preview 10. The app is a playground for experiments with .NET MAUI, called MAUI Beach, and will also appear in future articles. All code is available on GitHub: irongut/MauiBeach.

Start Me Up

To get the best experience with .NET MAUI Preview 10 requires the preview version of Visual Studio 2022 (17.1) which can be installed alongside the stable release of Visual Studio 2022 (17.0) or earlier releases. During installation select the Mobile development with .NET workload and you should see .NET MAUI (Preview) added to the optional mobile components on the right. Click install and you're ready to get started with .NET MAUI!

Visual Studio 2022 Installer

First Time

Creating a new .NET MAUI app in Visual Studio 2022 is a simple process. Select MAUI from the Project Types drop down and you will be presented with three options - .NET MAUI App, .NET MAUI Blazor App and .NET MAUI Class Library. Select .NET MAUI App and click Next.

VS2022 MAUI New Project

You will then be asked for a project name, location and solution name like any other .NET project. Click Create and a new cross platform app will be created from the .NET MAUI template.

VS2022 MAUI Project Details

Time to build the template app for the first time and check the emulators, etc are working. At this point I ran into a problem, my first build failed with three errors in the build output:

CS0246 The type or namespace name 'IStartup' could not be found
CS0246 The type or namespace name 'IAppHostBuilder' could not be found
CS0308 The non-generic type 'MauiApplication' cannot be used with type arguments

A quick search lead me to the cause of the problem. I had previously tried an early preview of Visual Studio 2022 and .NET MAUI which left old templates on my system and the new install hadn't replaced them. I was able to update the templates by running the following command line:

dotnet new --install  Microsoft.Maui.Templates

After deleting and recreating my project with the updated template I was able to build.

Run Through The Jungle

Having achieved my first build I ran the app on Windows, since that doesn't require an emulator or simulator. Immediately I was greeted by .NET Bot waving happily with a button connected to a counter, success! ๐ŸŽ‰

MAUI First Run on Windows

Similarly, running the app on Android went smoothly. Visual Studio 2022 had recognised the Android emulators I use with Visual Studio 2019 so I was able to pick one from the drop down, click run and a few moments later was greeted again by .NET Bot but now running on Android. ๐Ÿ‘

MAUI First Run on Android

The iOS build process and the Remote Simulator have been temperamental for me in recent months so I was unsurpried when my first iOS run failed with a deployment error:

> Need recipe file

Falling back on old Xamarin habits, I double checked the Start drop down had the Framework set to net6.0-ios and an iOS Simulator correctly selected then Cleaned and Rebuilt the solution.

VS2022 Start Framework Menu

That got me past the recipe file error and the Remote Simulator started up... but then failed with the message A fatal error occurred while trying to start the server. ๐Ÿ™

I closed the simulator and tried again... disconnected and reconnected to my build Mac and tried again... but to no avail. For some reason Visual Studio 2022 couldn't start the simulator. Eventually I used a Xamarin.iOS project in Visual Studio 2019 to start the simulator, switched back to Visual Studio 2022 and ran my MAUI app on the same simulator...

MAUI First Run on iOS

Success! ๐Ÿ˜

The recipe file error appears whenever I switch from testing on Windows or Android to testing on iOS. A clean and rebuild usually solves it, sometimes if done twice. ๐Ÿคจ Fortunately the simulator and Visual Studio 2022 have been behaving themselves since the first time and I haven't seen that problem again.

Paint It Black

With everything working on three platforms it was time to make some changes. In Resources/Styles/DefaultTheme.xaml I created a dark theme with a few colours and default styles for Label and Button based on existing styles in App.xaml:

<ResourceDictionary xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
                    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
                    x:Class="MauiBeach.Resources.Styles.DefaultTheme">

    <!-- Colour Palette -->

    <Color x:Key="pageBackgroundColor">#2E3440</Color>
    <Color x:Key="pageForegroundColor">#434C5E</Color>

    <Color x:Key="textColor">#D8DEE9</Color>
    <Color x:Key="strongTextColor">#ECEFF4</Color>
    <Color x:Key="altTextColor">White</Color>
    <Color x:Key="darkTextColor">#2E3440</Color>
    <Color x:Key="lightTextColor">#D8DEE9</Color>
    <Color x:Key="disabledTextColor">DarkGray</Color>

    <Color x:Key="primaryColor">#BF616A</Color>
    <Color x:Key="secondaryColor">#D08770</Color>
    <Color x:Key="tertiaryColor">#A3BE8C</Color>

    <!-- Styles -->

    <Style TargetType="Label">
        <Setter Property="TextColor"
                Value="{StaticResource textColor}"/>
        <Setter Property="FontFamily"
                Value="OpenSansRegular"/>
    </Style>

    <Style TargetType="Button">
        <Setter Property="TextColor"
                Value="{StaticResource altTextColor}"/>
        <Setter Property="FontFamily"
                Value="OpenSansRegular"/>
        <Setter Property="BackgroundColor"
                Value="{StaticResource primaryColor}"/>
        <Setter Property="CornerRadius"
                Value="10"/>
        <Setter Property="Padding"
                Value="15, 10"/>
    </Style>

</ResourceDictionary>

I created DefaultTheme.xaml by creating a new ContentPage and changing the type. This initially gave me an error because the ContentPage template was from Xamarin.Forms. Updating its using statements and xml namespace to MAUI instead of Forms, changing its Build Action property to MauiXaml and deleting the value of its Custom Tool property fixed the problem.

The existing styles in App.xaml I replaced by loading my theme:

<Application.Resources>
    <ResourceDictionary Source="Resources/Styles/DefaultTheme.xaml"/>
</Application.Resources>

I also added colours to Platforms/Android/Resources/values/colors.xml and updated the BackgroundColor of MainPage.xaml. I'll eventually expand on this theme, add more themes and a way to switch between them but for this article a simple dark theme will do. Running the app we can see that the theme works:

MAUI app with dark theme

Splish Splash

Setting up a theme in .NET MAUI works exactly like Xamarin.Forms so now I wanted to try something completely new - a cross platform splash screen. In Xamarin.Forms you need to add a splash screen for each platform in their own project and learn the different ways they all handle splash screens natively. With .NET MAUI you create one splash screen which the build system will magically make work on every platform!

There isn't much documentation for .NET MAUI at the moment but David Ortinau's awesome WeatherTwentyOne app is a great resource to learn from. You've may have seen it demonstrated at .Net Conf 2021 or in the official .Net MAUI blogs.

The project file *.csproj contains a MauiSplashScreen item which allows you to specify an image and a colour to use for the background of the splash screen. You can even use an SVG image so your splash screen will look good at all resolutions and screen sizes. I changed the default image to something suitable and the colour to the primary colour in my theme.

<MauiSplashScreen Include="Resources\wave_splash.svg" Color="#BF616A"/>

My splash screen worked perfectly on both Android and iOS. (Windows does not show a splash screen)

MAUI Beach Splash Screens on Android and iOS

The project file also contains a MauiIcon item which allows you to specify a background image, a foreground image and a colour. Again you can use SVG images and the build system will magically create icons for all platforms.

<MauiIcon Include="Resources\appicon.svg" ForegroundFile="Resources\wave_icon.svg" Color="#BF616A"/>

This worked quite well on Windows and iOS but unfortunately on Android the icon didn't look the same.

MAUI Beach Icons

I spent some time editing the image with Inkscape and a text editor (to remove meta tags the build process doesn't like) but I couldn't get an icon I liked on Android or even one that looked the same as the other platforms. The build process that creates the Android icon has some limitations at the moment and rejects valid SVG tags. I can create a better icon with Android Studio so I could fall back on using a platform specific icon for Android but hopefully the process will be improved in a future preview.

Shell Shocked

I want the UI for MAUI Beach to be based around a flyout menu built using Shell. Adding an Application.MainPage element containing a Shell element to App.xaml I defined a flyout header, an item template and three flyout items:

<Application.MainPage>
    <Shell FlyoutBehavior="Flyout"
           FlyoutHeaderBehavior="Fixed"
           FlyoutVerticalScrollMode="Auto">

        <Shell.FlyoutHeader>
            <Grid BackgroundColor="{StaticResource primaryColor}"
                  HeightRequest="220"
                  Padding="0, 10">

                <Image Source="wave_icon.png"
                       HorizontalOptions="Center"
                       VerticalOptions="Center"
                       HeightRequest="200"
                       WidthRequest="200"/>

                <Label Text="MAUI Beach"
                       TextColor="{StaticResource darkTextColor}"
                       FontSize="42"
                       FontAttributes="Bold"
                       HorizontalOptions="Center"
                       VerticalOptions="End"/>

            </Grid>
        </Shell.FlyoutHeader>

        <Shell.ItemTemplate>
            <DataTemplate>
                <Grid ColumnDefinitions="0.25*,0.75*"
                      Padding="0, 10">

                    <Image Source="{Binding FlyoutIcon}"
                           HeightRequest="45"
                           HorizontalOptions="Center"/>

                    <Label Grid.Column="1"
                           Text="{Binding Title}"
                           FontSize="Large"
                           FontAttributes="Bold"
                           VerticalOptions="Center"/>

                </Grid>
            </DataTemplate>
        </Shell.ItemTemplate>

        <FlyoutItem Title="Home"
                    Icon="home.png">
            <ShellContent ContentTemplate="{DataTemplate p:HomePage}"/>
        </FlyoutItem>

        <FlyoutItem Title="Settings"
                    Icon="settings.png">
            <ShellContent ContentTemplate="{DataTemplate p:SettingsPage}"/>
        </FlyoutItem>

        <FlyoutItem Title="About"
                    Icon="about.png">
            <ShellContent ContentTemplate="{DataTemplate p:AboutPage}"/>
        </FlyoutItem>

    </Shell>
</Application.MainPage>

One thing to note here is the names of the images used in my flyout items - wave_icon.png, home.png, settings.png and about.png. You will not find those images in my project but in the Resources/Images folder you will find wave_icon.svg, home.svg, settings.svg and about.svg - so what is going on? .NET MAUI incorporates a tool called ResizetizerNT which allows you to add a single SVG or PNG image to your project and at build time it creates all the different sizes of image required by your app in the native resources folders. So about.svg in the Resources/Images folder becomes about.png in my XAML. ResizetizerNT is also invloved in the creation of app icons during the build process.

In App.xaml.cs I removed the line that created the default MainPage and deleted the files. At this point I needed to create my three pages and when creating a new ContentPage I ran into the same Xamarin.Forms template issue as earlier with the ResourceDictionary. Again updating their using statements and xml namespaces, changing Build Action to MauiXaml and deleting Custom Tool fixed the problem.

My initial pages are almost identical and very basic for now:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MauiBeach.Pages.HomePage"
             Title="Home">
    <ContentPage.Content>
        <StackLayout>
            <Label Text="Home Page"
                   FontSize="Large"
                   VerticalOptions="CenterAndExpand" 
                   HorizontalOptions="CenterAndExpand"/>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

After checking that worked I could start to customise my UI more. To my theme in Resources/Styles/DefaultTheme.xaml I added default styles for Page and NavigationPage and styles for the Shell flyout menu and menu items.

<Style TargetType="Page"
       ApplyToDerivedTypes="True">
    <Setter Property="BackgroundColor"
            Value="{StaticResource pageBackgroundColor}"/>
    <Setter Property="Padding"
            Value="20, 10"/>
</Style>

<Style TargetType="NavigationPage"
        ApplyToDerivedTypes="True">
    <Setter Property="BackgroundColor"
            Value="{StaticResource pageBackgroundColor}"/>
    <Setter Property="BarBackgroundColor"
            Value="{StaticResource primaryColor}"/>
    <Setter Property="BarTextColor"
            Value="{StaticResource altTextColor}"/>
</Style>

<!-- Shell -->

<Style x:Key="BaseStyle"
       TargetType="Element">
    <Setter Property="Shell.FlyoutBackground"
            Value="{StaticResource pageBackgroundColor}"/>
    <Setter Property="Shell.FlyoutBackgroundColor"
            Value="{StaticResource pageBackgroundColor}"/>
    <Setter Property="Shell.BackgroundColor"
            Value="{StaticResource primaryColor}"/>
    <Setter Property="Shell.ForegroundColor"
            Value="{StaticResource altTextColor}"/>
    <Setter Property="Shell.TitleColor"
            Value="{StaticResource altTextColor}"/>
    <Setter Property="Shell.DisabledColor"
            Value="{StaticResource disabledTextColor}"/>
    <Setter Property="Shell.UnselectedColor"
            Value="{StaticResource disabledTextColor}"/>
    <Setter Property="Shell.TabBarBackgroundColor"
            Value="{StaticResource primaryColor}"/>
    <Setter Property="Shell.TabBarTitleColor"
            Value="{StaticResource altTextColor}"/>
    <Setter Property="Shell.TabBarUnselectedColor"
            Value="{StaticResource disabledTextColor}"/>
    <Setter Property="Shell.NavBarHasShadow"
            Value="True"/>
</Style>

<Style BasedOn="{StaticResource BaseStyle}"
       ApplyToDerivedTypes="True"
       TargetType="FlyoutItem"/>

<Style x:Key="shellItem"
       TargetType="Grid">
    <Setter Property="VisualStateManager.VisualStateGroups">
        <VisualStateGroupList>
            <VisualStateGroup x:Name="CommonStates">
                <VisualState x:Name="Normal">
                    <VisualState.Setters>
                        <Setter Property="BackgroundColor"
                                Value="{StaticResource pageBackgroundColor}"/>
                    </VisualState.Setters>
                </VisualState>
                <VisualState x:Name="Selected">
                    <VisualState.Setters>
                        <Setter Property="BackgroundColor"
                                Value="{StaticResource secondaryColor}"/>
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateGroupList>
    </Setter>
</Style>

I also expanded on my Shell definition in App.xaml by adding a flyout footer and a gradient backdrop:

<Shell.FlyoutFooter>
    <Grid BackgroundColor="{StaticResource primaryColor}"
          HeightRequest="32"
          Padding="0, 5, 0, 5">

        <Label Text="Copyright ยฉ 2021 Taranis Software"
               TextColor="{StaticResource altTextColor}"
               FontSize="Small"
               HorizontalOptions="Center"/>

    </Grid>
</Shell.FlyoutFooter>

<Shell.FlyoutBackdrop>
    <LinearGradientBrush StartPoint="0,0"
                         EndPoint="0,1">
        <GradientStop Color="{StaticResource primaryColor}"
                      Offset="0.3"/>
        <GradientStop Color="{StaticResource secondaryColor}"
                      Offset="1.0"/>
    </LinearGradientBrush>
</Shell.FlyoutBackdrop>

As you can see this mostly worked but the FlyoutBackground / FlyoutBackgroundColor properties are not correctly set on any platform, instead these areas are white or black depending on the device's current system theme. Looking at the .NET MAUI repo this has been fixed already for Android and iOS and work is in progress for Windows so a future preview version will fix my flyout menu.

MAUI Beach Shell Flyout Menu

The End

I now have the basic framework set up for MAUI Beach - a cross platform app built using .NET MAUI. My app has a theme, a splash screen, an icon and a flyout menu with three empty pages.

I have run into a few issues, which should be expected when working with pre-release code, but none of them were insurmountable and most things worked in a similar way to Xamarin.Forms. .NET MAUI Preview 11 is due soon and should fix some of my problems. Overall I think .NET MAUI is shaping up nicely and I'm looking forward to a great experience when it goes GA in Q2 2022.

ย 

ย 

Cover image includes a background vector created by freepik from www.freepik.com.

Did you find this article valuable?

Support Dave Murray by becoming a sponsor. Any amount is appreciated!

ย