Xamarin.Forms FlyoutHeader and iOS safe area
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:
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:
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:
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.