Xamarin.Forms FlyoutHeader and iOS safe area

February 19th 2021 Xamarin iOS

In Xamarin.Forms Shell, the flyout header is always positioned at the top of the flyout even if not all menu items fit on the screen and scrolling is required. It's a great place to show an application logo or the avatar of the logged-in user.

The content to render in the header must be assigned to the Shell FlyoutHeader property:

<Shell.FlyoutHeader>
  <Grid BackgroundColor="LightBlue">
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="*" />
      <ColumnDefinition Width="80" />
      <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
      <RowDefinition Height="20" />
      <RowDefinition Height="80" />
      <RowDefinition Height="20" />
    </Grid.RowDefinitions>
    <BoxView Grid.Column="1"
             Grid.Row="1"
             BackgroundColor="{StaticResource Primary}"
             CornerRadius="40" />
  </Grid>
</Shell.FlyoutHeader>

On iOS, it is by default rendered in the safe area, making sure that it is not obscured by the device notch or the status bar:

Flyout header rendered in iOS safe area

At the page level, Xamarin.Forms provides an iOS-specific UseSafeArea property to toggle between rendering only in the safe area or also outside of it.

However, I couldn't find a way to apply this property to the flyout or its header. By pure coincidence, I found out that setting the margin on the flyout header root control overrides this default behavior:

<Shell.FlyoutHeader>
  <Grid BackgroundColor="LightBlue"
        Margin="0">
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="*" />
      <ColumnDefinition Width="80" />
      <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
      <RowDefinition Height="20" />
      <RowDefinition Height="80" />
      <RowDefinition Height="20" />
    </Grid.RowDefinitions>
    <BoxView Grid.Column="1"
             Grid.Row="1"
             BackgroundColor="{StaticResource Primary}"
             CornerRadius="40" />
  </Grid>
</Shell.FlyoutHeader>

It causes the rendering to ignore the iOS safe area settings:

Flyout header rendered outside iOS safe area

Depending on your design and requirements, this might be exactly what you want. In my case, it wasn't. I originally wanted to use the margin for the header contents layout. Since it didn't work as expected on iOS, I used a grid for my layout needs instead.

You might wonder how this affects Android? The margin works as expected there. Not setting it or setting it to 0 results in the same output:

Flyout header rendering on Android

That's good, since it allows you to change the behavior on iOS without affecting Android.

A sample demonstrating this behavior is available in my GitHub repository. In the latest commit, the flyout header is rendered outside the iOS safe zone and in the one before that it's rendered inside the safe zone.

Although there's no specific property to specify whether to render the flyout header inside or outside the safe zone on iOS, this behavior can be changed by setting or not setting the margin property of the flyout header root element. This means that the property can't be used for header layout if you want to keep the header inside the safe area on iOS. But there are other ways to implement a layout, e.g. with a grid as I did in my example.

Get notified when a new blog post is published (usually every Friday):

Copyright
Creative Commons License