dotnet / dotnet/maui

[Android] Border with Rotation and HorizontalOptions.Start/End positioned incorrectly on initial load

Open
#33,530 1 comment 1 reaction 0 assignees View on GitHub
area-controls-border platform/android s/triaged s/verified t/bug
Dominant language
C#
Stars
23.3k
Forks
2k
Avg merge
1d 15h
Merged PRs (30d)
290

Description

### Description

When a Border element has both `Rotation` (90° or -90°) AND `HorizontalOptions="Start"` or `HorizontalOptions="End"` set during initial load in XAML, Android positions the element incorrectly. The element appears partially off-screen, and the Shadow is decoupled from the Border itself (appearing in a different location).

iOS renders the same layout correctly.

**Visual Symptoms on Android:**
- Border is positioned incorrectly (e.g., center of element at screen edge instead of edge touching screen edge)
- Shadow appears in a different location than the rotated Border
- Setting `TranslationX` directly on the element does NOT fix the issue
- Calling `InvalidateMeasure()` does NOT fix the issue

**Root Cause:**
The bug only occurs when the rotated element is **loaded with these properties from the start**. If the element starts with `Rotation="0"` and `HorizontalOptions="Center"`, and these properties are changed programmatically after layout completes, the positioning works correctly on both platforms.

This suggests Android's layout engine calculates the initial position incorrectly when Rotation and non-Center LayoutOptions are combined during the measure/arrange pass.

### Steps to Reproduce

1. Create a new .NET MAUI project
2. Create a ContentPage with `BackgroundColor="Transparent"`
3. Add a Border with the following properties:
- `HorizontalOptions="Start"`
- `VerticalOptions="Center"`
- `Rotation="-90"`
- Include a Shadow: ``
4. Add complex content inside the Border (e.g., VerticalStackLayout with BoxView + Label)
5. Push the page modally: `await Navigation.PushModalAsync(page, false);`
6. Run on Android device/emulator

**Expected outcome:**
Border is positioned at the left edge of the screen, rotated -90°, with the visual edge touching the screen edge. Shadow is aligned with the Border. Identical to iOS behavior.

**Actual outcome on Android:**
Border is positioned incorrectly (partially off-screen), and Shadow appears decoupled from the Border in a different location.

**Actual outcome on iOS:**
Works correctly as expected

### Link to public reproduction project repository

https://github.com/paulober/maui-android-modal-layout-calculation-bug

### Version with bug

10.0.20

### Is this a regression from previous behavior?

Not sure, did not test other versions

### Last version that worked well

_No response_

### Affected platforms

Android

### Affected platform versions

Android (API36) and possible earlier

### Did you find any workaround?

Yes, the following workaround fixes the issue:

**In XAML:** Set initial state to non-rotated and centered:
```xaml

```
**In Code-Behind (OnAppearing):** Apply rotation AFTER layout completes:
```cs
protected override async void OnAppearing()
{
base.OnAppearing();

// Wait for initial layout to complete
await Task.Delay(200);

// NOW set the actual rotation and position
BadgeBox.HorizontalOptions = LayoutOptions.Start;
BadgeBox.Rotation = -90;
await Task.Delay(1);

// Calculate TranslationX offset if needed
// e.g. for displaying a badge that has a machine readable code and you therefore need placement where I is accessible by the reader
CalculateBadgePosition();
}
```

### Relevant log output

```shell

```

Contributor guide

Open the contributing guide

Research direction

Start by running the linked public reproduction project on Android and inspect the ContentPage containing the rotated Border, its Shadow, and the modal navigation path. Trace the Android measure/arrange behavior for initial Rotation with HorizontalOptions.Start or End, comparing it with iOS; done means the Border and Shadow align at the expected screen edge during initial load.

Written by the indexing model from the issue text.

Assessment

Tech stack
android, csharp
Domain
mobile
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.