dotnet / maui

.NET MAUI is the .NET Multi-platform App UI, a framework for building native device applications spanning mobile, tablet, and desktop.
https://dot.net/maui
MIT License
22.21k stars 1.75k forks source link

Shell Title View Overlaps Status Bar on Android when Layout Limits removed #24694

Open asi-evin opened 1 month ago

asi-evin commented 1 month ago

Description

When Android settings remove status bar from the layout (i.e. a transparent status bar similar to iOS), the Shell Title Text is either uncomfortably close or overlaps.

Steps to Reproduce

  1. Create a new Project.

  2. Add the following lifecycle events to "MauiProgram.cs":

    builder
    .UseMauiApp<App>()
    #if ANDROID
    .ConfigureLifecycleEvents(lifecycleBuilder =>
    {
        lifecycleBuilder.AddAndroid(a =>
            a.OnCreate((activity, state) =>
            {
                activity.Window?.AddFlags(WindowManagerFlags.LayoutNoLimits);
            })
        );
    })
    #endif
    .ConfigureFonts(fonts =>
    {
        fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
        fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
    });
  3. Add the following properties to "AppShell.xaml" (for clearer picture):

BackgroundColor="{StaticResource Primary}"
TitleColor="White"

Link to public reproduction project repository

None. Simple enough to create from starting template.

Version with bug

8.0.82 SR8.2

Is this a regression from previous behavior?

Yes, this used to work in Xamarin.Forms

Last version that worked well

Unknown/Other

Affected platforms

Android

Affected platform versions

No response

Did you find any workaround?

None, other than a custom title view with padding, but would prefer to stick with defaults.

Screenshots

Limits Removed Custom Title Default Status
Screenshot_20240910_120543 1 Screenshot_20240910_122041 1 Screenshot_20240910_120628 1
github-actions[bot] commented 1 month ago

Hi I'm an AI powered bot that finds similar issues based off the issue title.

Please view the issues below to see if they solve your problem, and if the issue describes your problem please consider closing this one and thumbs upping the other issue to help us prioritize it. Thank you!

Open similar issues:

Closed similar issues:

Note: You can give me feedback by thumbs upping or thumbs downing this comment.

asi-evin commented 1 month ago

If you need help with the "safe areas" to move the title or shell view down, you can use the following code snippet:

public Thickness SafeAreaPadding 
{
    get
    {
        if (OperatingSystem.IsAndroidVersionAtLeast(21))
        {
            var insets = ViewCompat.GetRootWindowInsets(Platform.CurrentActivity.Window.DecorView);
            var systemBarAndNotchInsets =
                insets.GetInsets(WindowInsetsCompat.Type.SystemBars() | WindowInsetsCompat.Type.DisplayCutout());

            var density = DeviceDisplay.Current.MainDisplayInfo.Density;

            return new Thickness(systemBarAndNotchInsets.Left / density, systemBarAndNotchInsets.Top / density,
                systemBarAndNotchInsets.Right / density, systemBarAndNotchInsets.Bottom * density);
        }

        //todo: older stuff...
    }
}
mattleibow commented 1 month ago

I am not sure what the issue is here. You are removing the status bar and making the content fill the screen. That appears to be what is happening?

Can you show what happened in Xamarin.Forms that is not happening in MAUI?

Also, what are you expecting to happen when you remove the layout limits?

I know it is a bit of a pain, but since you have the sample project already to take the screenshots, could you attach it to the issue to make debugging easier for the people that will be testing this issue?

asi-evin commented 1 month ago

The status bar is not "removed", but it's no longer pushing the content down. I expect the TitleView/NavigationBar Text to be pushed down like it would be on iOS. See this closed issue from Xamarin.forms:

https://github.com/xamarin/Xamarin.Forms/pull/1116

mattleibow commented 1 month ago

That PR is using the translucent flags, you are requesting no status bar. What happens if you use the translucent flag instead?

asi-evin commented 1 month ago

That PR is using the translucent flags, you are requesting no status bar. What happens if you use the translucent flag instead?

Then the status bar pushes the content down. I do want content to appear under the status bar for certain pages, and then content I control I do accomodate for, similar to how you have to accomodate content around the status bar on iOS.

The Shell Navigation Bar should have the background fill behind the status bar with the text showing below it. If you're wondering where this is coming from, it's because the Community Toolkit allows you to set the Status Bar color as transparent, I just tried to dial down exactly why the issue is happening. I don't think the issue is with the Toolkit, it's just that MAUI is not accomading this edge case, which will be even MORE apparent come android 15 when edge-to-edge will be the default.

image image

asi-evin commented 1 month ago

I managed to find the following work-a-round:

using Microsoft.Extensions.Logging;

#if ANDROID
using Microsoft.Maui.LifecycleEvents;
using Android.Views;
using AndroidX.Core.View;
using Google.Android.Material.AppBar;
#endif

namespace ShellTitleNavBug;

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
#if ANDROID
            .ConfigureLifecycleEvents(lifecycleBuilder =>
            {
                lifecycleBuilder.AddAndroid(a =>
                    a.OnCreate((activity, state) =>
                    {
                        activity.Window?.AddFlags(WindowManagerFlags.LayoutNoLimits);
                    })
                );
            })
            .ConfigureMauiHandlers(handlers =>
            {
                Microsoft.Maui.Handlers.ToolbarHandler.Mapper.AppendToMapping("CustomNavigationView", (handler, view) =>
                {
                    var toolbar = handler.PlatformView;

                    //Listener doesn't seem to work with toolbar, so use the main window's decor view instead
                    ViewCompat.SetOnApplyWindowInsetsListener(Platform.CurrentActivity.Window.DecorView, new ToolbarCustomListener(toolbar));
                });
            })
#endif
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
            });

#if DEBUG
        builder.Logging.AddDebug();
#endif

        return builder.Build();
    }

#if ANDROID
    private class ToolbarCustomListener : Java.Lang.Object, IOnApplyWindowInsetsListener
    {
        private MaterialToolbar _toolbar;

        private int _defaultPaddingLeft;
        private int _defaultPaddingTop;
        private int _defaultPaddingRight;
        private int _defaultPaddingBottom;

        private int _defaultMinHeight;

        public ToolbarCustomListener(MaterialToolbar toolbar)
        {
            _toolbar = toolbar;

            _defaultPaddingLeft = _toolbar.PaddingLeft;
            _defaultPaddingTop = _toolbar.PaddingTop;
            _defaultPaddingRight = _toolbar.PaddingRight;
            _defaultPaddingBottom = _toolbar.PaddingBottom;

            _defaultMinHeight = _toolbar.MinimumHeight;
        }

        public WindowInsetsCompat OnApplyWindowInsets(Android.Views.View v, WindowInsetsCompat insets)
        {
            var systemInsets =
                insets.GetInsets(WindowInsetsCompat.Type.StatusBars() | WindowInsetsCompat.Type.DisplayCutout());

            _toolbar.SetPadding(_defaultPaddingLeft, _defaultPaddingTop + systemInsets.Top, _defaultPaddingRight, _defaultPaddingBottom);
            _toolbar.LayoutParameters.Height = _defaultMinHeight + systemInsets.Top;

            return WindowInsetsCompat.Consumed;
        }
    }
#endif
}