SafeAreaHelper
Static helper for getting safe area insets on devices with notches, rounded corners, or system bars.
Namespace: GamaLearn.Helpers
Assembly: GamaLearn.Maui.Core
Overview
SafeAreaHelper provides platform-specific methods to retrieve safe area insets, ensuring your UI doesn't overlap with system UI elements like notches, status bars, navigation bars, and rounded corners. Critical for modern devices like iPhone 14 Pro, Pixel 7, and other edge-to-edge displays.
Installation
dotnet add package GamaLearn.Maui.Core
Methods
GetSafeAreaInsets()
Gets the safe area insets for the current window.
Returns: Thickness (Left, Top, Right, Bottom) in device-independent units
using GamaLearn.Helpers;
Thickness insets = SafeAreaHelper.GetSafeAreaInsets();
// Example: { Left=0, Top=59, Right=0, Bottom=34 } on iPhone 14 Pro
GetSafeAreaInsets(Page page)
Gets the safe area insets for a specific page.
Parameters:
page- The page to get insets for
Returns: Thickness representing safe area insets
Thickness insets = SafeAreaHelper.GetSafeAreaInsets(this);
ApplySafeAreaPadding(Layout layout, SafeAreaEdges edges = SafeAreaEdges.All)
Applies safe area padding to a layout by adding insets to current padding.
Parameters:
layout- The layout to apply padding toedges- Which edges to apply padding to (default: all)
SafeAreaHelper.ApplySafeAreaPadding(myStackLayout);
// Only apply to top and bottom
SafeAreaHelper.ApplySafeAreaPadding(myStackLayout, SafeAreaEdges.Top | SafeAreaEdges.Bottom);
ApplySafeAreaPadding(Page page, SafeAreaEdges edges = SafeAreaEdges.All)
Applies safe area padding to a page.
SafeAreaHelper.ApplySafeAreaPadding(this);
GetStatusBarHeight()
Gets the status bar height.
Returns: Status bar height in device-independent units
double statusBarHeight = SafeAreaHelper.GetStatusBarHeight();
// Example: 59 on iPhone 14 Pro, 24 on most Android devices
GetNavigationBarHeight()
Gets the navigation bar height (bottom system UI on Android, home indicator on iOS).
Returns: Navigation bar height in device-independent units
double navBarHeight = SafeAreaHelper.GetNavigationBarHeight();
// Example: 34 on iPhone 14 Pro, 48 on Android with gesture nav
SafeAreaEdges Enum
[Flags]
public enum SafeAreaEdges
{
None = 0,
Left = 1,
Top = 2,
Right = 4,
Bottom = 8,
All = Left | Top | Right | Bottom
}
Usage Examples
Basic Page Setup
public partial class MyPage : ContentPage
{
public MyPage()
{
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
// Apply safe area padding to the page
SafeAreaHelper.ApplySafeAreaPadding(this);
}
}
Full-Screen Content with Safe Areas
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyApp.FullScreenPage"
BackgroundColor="Black">
<Grid>
<!-- Background image extends to edges -->
<Image Source="background.jpg" Aspect="AspectFill" />
<!-- Content respects safe areas -->
<VerticalStackLayout x:Name="ContentLayout"
VerticalOptions="Fill">
<Label Text="Title" FontSize="32" TextColor="White" />
<Label Text="Content" />
</VerticalStackLayout>
</Grid>
</ContentPage>
protected override void OnAppearing()
{
base.OnAppearing();
SafeAreaHelper.ApplySafeAreaPadding(ContentLayout);
}
Selective Safe Area Application
public class CustomHeaderPage : ContentPage
{
protected override void OnAppearing()
{
base.OnAppearing();
var header = this.FindByName<StackLayout>("Header");
var footer = this.FindByName<StackLayout>("Footer");
// Only apply to top and bottom
SafeAreaHelper.ApplySafeAreaPadding(header, SafeAreaEdges.Top);
SafeAreaHelper.ApplySafeAreaPadding(footer, SafeAreaEdges.Bottom);
}
}
Dynamic Layout Adjustments
public class AdaptiveViewModel : ObservableObject
{
private double topPadding;
public double TopPadding
{
get => topPadding;
set => SetProperty(ref topPadding, value);
}
private double bottomPadding;
public double BottomPadding
{
get => bottomPadding;
set => SetProperty(ref bottomPadding, value);
}
public void UpdateSafeAreaPadding(Page page)
{
Thickness insets = SafeAreaHelper.GetSafeAreaInsets(page);
TopPadding = insets.Top;
BottomPadding = insets.Bottom;
}
}
<ContentPage>
<VerticalStackLayout Padding="{Binding TopPadding, 0, BottomPadding, 0}">
<Label Text="Content" />
</VerticalStackLayout>
</ContentPage>
Status Bar Overlay
public class TransparentStatusBarPage : ContentPage
{
protected override void OnAppearing()
{
base.OnAppearing();
// Get status bar height
double statusBarHeight = SafeAreaHelper.GetStatusBarHeight();
// Adjust header to account for transparent status bar
var header = this.FindByName<Grid>("Header");
header.Padding = new Thickness(0, statusBarHeight, 0, 0);
}
}
Floating Action Button
<ContentPage>
<AbsoluteLayout>
<VerticalStackLayout AbsoluteLayout.LayoutBounds="0,0,1,1"
AbsoluteLayout.LayoutFlags="All">
<!-- Main content -->
</VerticalStackLayout>
<!-- FAB positioned with safe area consideration -->
<Button x:Name="Fab"
Text="+"
AbsoluteLayout.LayoutBounds="0.95,0.95,60,60"
AbsoluteLayout.LayoutFlags="PositionProportional"
CornerRadius="30" />
</AbsoluteLayout>
</ContentPage>
protected override void OnAppearing()
{
base.OnAppearing();
double navBarHeight = SafeAreaHelper.GetNavigationBarHeight();
// Adjust FAB position to avoid navigation bar
AbsoluteLayout.SetLayoutBounds(Fab,
new Rect(0.95, 0.95, 60, 60 + navBarHeight));
}
Modal with Safe Areas
public class ModalPage : ContentPage
{
public ModalPage()
{
InitializeComponent();
// Full screen modal
Shell.SetPresentationMode(this, PresentationMode.FullScreen);
}
protected override void OnAppearing()
{
base.OnAppearing();
// Apply safe area to ensure close button is visible
SafeAreaHelper.ApplySafeAreaPadding(this);
}
}
Landscape Orientation
public class VideoPlayerPage : ContentPage
{
private bool isPortrait = true;
protected override void OnSizeAllocated(double width, double height)
{
base.OnSizeAllocated(width, height);
bool wasPortrait = isPortrait;
isPortrait = height > width;
// Reapply safe area when orientation changes
if (wasPortrait != isPortrait)
{
UpdateSafeAreas();
}
}
private void UpdateSafeAreas()
{
Thickness insets = SafeAreaHelper.GetSafeAreaInsets(this);
// In landscape, notches may be on left/right
var playerControls = this.FindByName<Grid>("PlayerControls");
playerControls.Padding = insets;
}
}
Best Practices
1. Apply on OnAppearing
✅ Good:
protected override void OnAppearing()
{
base.OnAppearing();
SafeAreaHelper.ApplySafeAreaPadding(this);
}
❌ Avoid:
public MyPage()
{
InitializeComponent();
SafeAreaHelper.ApplySafeAreaPadding(this); // Too early, may not have handler
}
2. Reapply on Orientation Changes
✅ Good:
protected override void OnSizeAllocated(double width, double height)
{
base.OnSizeAllocated(width, height);
SafeAreaHelper.ApplySafeAreaPadding(this);
}
❌ Avoid:
// Only applying once, doesn't adapt to rotation
3. Combine with Existing Padding
✅ Good:
// ApplySafeAreaPadding adds to existing padding
myLayout.Padding = new Thickness(16); // Design padding
SafeAreaHelper.ApplySafeAreaPadding(myLayout); // Adds safe area
❌ Avoid:
// This overwrites design padding
Thickness insets = SafeAreaHelper.GetSafeAreaInsets();
myLayout.Padding = insets;
Platform Behavior
iOS / MacCatalyst
- Returns insets from
UIWindow.SafeAreaInsets - Includes notch, Dynamic Island, status bar, home indicator
- Example iPhone 14 Pro: Top=59, Bottom=34
Android
- Returns combined system bars + display cutout insets
- Includes status bar, navigation bar, notches, punch holes
- Adapts to gesture navigation vs. button navigation
Windows
- Returns title bar height at top
- No insets on other edges (no notches on Windows devices)
Tizen
- Returns zero (no system UI overlays)
Troubleshooting
Insets Always Zero
Call ApplySafeAreaPadding in OnAppearing, not in the constructor:
protected override void OnAppearing()
{
base.OnAppearing();
SafeAreaHelper.ApplySafeAreaPadding(this);
}
Content Still Overlaps System UI
Ensure you're applying to the correct layout. The layout must be at the root level of your page content.
Excessive Padding
Don't apply safe area padding multiple times:
❌ Avoid:
SafeAreaHelper.ApplySafeAreaPadding(myLayout);
SafeAreaHelper.ApplySafeAreaPadding(myLayout); // Applied twice!
See Also
- KeyboardHelper - Keyboard management
- DeviceInfoService - Device information
- BatteryService - Battery monitoring