#22243·Avalonia

ContentPage command bars are drawn under the system bars (SafeAreaPadding is applied to the content only)

Author: zdpcdtCreated Sep 16, 2026Updated Sep 16, 2026
Labelsbug

Describe the bug

ContentPage applies Page.SafeAreaPadding to PART_ContentPresenter only. TopCommandBar and BottomCommandBar are docked to the top and bottom edges of the template's DockPanel and never receive any padding, so their content is rendered underneath the status bar and the home indicator area.

This shows up on Android/iOS whenever the page is hosted in a PageNavigationHost (which sets Page.SafeAreaPadding from IInsetsManager) and has a command bar. Without command bars the same page looks correct, because the content presenter spans the whole page and absorbs the insets with its padding.

Note that AutomaticallyApplySafeAreaPadding does not help here: setting it to false only hands the plain Padding back to the content presenter, it still has no effect on the bars.

To Reproduce

Repro project: https://github.com/zdpcdt/AvaloniaIssues/tree/main/ContentPagePadding

  1. Use a ContentPage inside a PageNavigationHost on Android or iOS, with AutomaticallyApplySafeAreaPadding left at its default of true.
  2. Assign a TopCommandBar and/or a BottomCommandBar whose content has a visible background, e.g.:
xml
<ContentPage>
  <ContentPage.TopCommandBar>
    <ContentControl Content="TopCommandBar" Background="Green" />
  </ContentPage.TopCommandBar>
  <ContentPage.BottomCommandBar>
    <ContentControl Content="BottomCommandBar" Background="Red" />
  </ContentPage.BottomCommandBar>
  <TextBlock Text="Content" />
</ContentPage>
  1. Run the app. The green bar sits at the very top of the screen behind the status bar and the red bar at the very bottom behind the home indicator, while the content between them is inset correctly.

The same can be reproduced without a device by setting SafeAreaPadding explicitly, e.g. SafeAreaPadding="0,44,0,34".

Expected behavior

Each command bar should absorb the inset of the edge it is docked to — top inset for the top bar, bottom inset for the bottom bar — and the content should only absorb the insets that no bar already took. This is the rule the other page types already follow: NavigationPage pads its navigation bar with the top inset and passes the remainder down to the child page, and TabbedPage splits the insets between the tab strip and the active page.

Avalonia version

12.1.2

OS

Android

Additional context

Workaround used so far — inset the whole page and manage the insets manually:

xml
<ContentPage AutomaticallyApplySafeAreaPadding="False"
             Margin="{Binding $self.SafeAreaPadding}">

This works, but the page background no longer paints behind the inset areas, and AutomaticallyApplySafeAreaPadding can no longer be left at its default.

https://github.com/user-attachments/assets/ad0d0381-a963-415e-88b4-961529bed9a3