maui icon indicating copy to clipboard operation
maui copied to clipboard

MAUI: Shell FlyoutHeader disables the click/selection of first Flyout item on iOS

Open nsood9 opened this issue 1 year ago • 6 comments

Description

I'm creating a MAUI Shell app with Flyout and tabs. As soon as I add a Flyout header to the Flyout, it disables the selection of first Flyout item on iOS while other items are still selectable. On android, it works fine. Removing the Flyoutheader fixes this issue. But I need to show a header view in the Flyout.

Steps to Reproduce

Sample code:

`<Shell xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:YourNamespace" x:Class="YourNamespace.AppShell">

<Shell.Resources>
    <!-- Define your styles here -->
    <Style x:Key="BaseStyle" TargetType="Element">
        <!-- Define your base styles -->
    </Style>

    <Style x:Key="ShellItemStyle" TargetType="Element" BasedOn="{StaticResource BaseStyle}">
        <!-- Define styles for ShellItems -->
    </Style>

    <Style x:Key="TabStyle" TargetType="Element" BasedOn="{StaticResource BaseStyle}">
        <!-- Define styles for Tabs -->
    </Style>
</Shell.Resources>
<Shell.FlyoutHeaderTemplate>
    <DataTemplate>
        <StackLayout BackgroundColor="#53A0FD" Padding="12,24,12,12">
            <!-- Your header content goes here -->
            <Image Source="your_logo.png" HeightRequest="80" WidthRequest="80" />
            <Label Text="Your App Name" TextColor="White" FontSize="20" FontAttributes="Bold" />
        </StackLayout>
    </DataTemplate>
</Shell.FlyoutHeaderTemplate>

<!-- FlyoutItem 1: Today -->
<FlyoutItem Title="Today" Route="Today">
    <Tab Title="Calendar" Route="Calendar">
        <ShellContent ContentTemplate="{DataTemplate local:CalendarPage}" />
    </Tab>
    <Tab Title="Alerts" Route="Alerts">
        <ShellContent ContentTemplate="{DataTemplate local:AlertsPage}" />
    </Tab>
    <Tab Title="Messages" Route="Messages">
        <ShellContent ContentTemplate="{DataTemplate local:MessagesPage}" />
    </Tab>
    <Tab Title="More" Route="More">
        <ShellContent ContentTemplate="{DataTemplate local:MorePage}" />
    </Tab>
</FlyoutItem>

<!-- FlyoutItem 2: Logout -->
<FlyoutItem Title="Logout" Route="Logout">
    <ShellContent ContentTemplate="{DataTemplate local:LogoutPage}" />
</FlyoutItem>

<!-- FlyoutItem 3: Tenants -->
<FlyoutItem Title="Tenants" Route="Tenants">
    <ShellContent ContentTemplate="{DataTemplate local:TenantsPage}" />
</FlyoutItem>
`

Link to public reproduction project repository

No response

Version with bug

8.0.3

Is this a regression from previous behavior?

Not sure, did not test other versions

Last version that worked well

Unknown/Other

Affected platforms

iOS

Affected platform versions

No response

Did you find any workaround?

Didn't find any workaround yet.

Relevant log output

No response

nsood9 avatar Jan 05 '24 09:01 nsood9

I had a similar issue with flyout header on ios where it wasnt sizing properly when not using safearea. Ended up removing it and just adding a header to the top of <Shell.FlyoutContent> which uses a grid.

jeff-eats-pubsubs avatar Jan 09 '24 16:01 jeff-eats-pubsubs

Hi @jeff-eats-pubsubs , can you share a sample of the code to see how you've added the header and other flyout options to the FlyoutContent? It will be really helpful. Thanks in advance!

nsood9 avatar Jan 09 '24 17:01 nsood9

Having the same issue. This is the simple code I have: `

<Shell x:Class="IOTToolkit.AppShell" xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:connections="clr-namespace:IOTToolkit.Views.Connections" xmlns:local="clr-namespace:IOTToolkit" xmlns:settings="clr-namespace:IOTToolkit.Views.Settings" Shell.FlyoutBehavior="Flyout" Shell.ForegroundColor="{AppThemeBinding Dark={StaticResource White}, Light={StaticResource OffBlack}}" Title="IOTToolkit" x:Name="shell" x:DataType="local:AppShell">

<ShellContent
        Title="Home"
        ContentTemplate="{DataTemplate local:MainPage}"
        Route="MainPage" />

<ShellContent
        Title="Local connection"
        ContentTemplate="{DataTemplate connections:LocalConnectionPage}"
        Route="LocalConnectionPage" />

<ShellContent
        Title="Cloud"
        ContentTemplate="{DataTemplate connections:CloudConnectionPage}"
        Route="CloudConnectionPage" />

<ShellContent
        Title="Settings"
        ContentTemplate="{DataTemplate settings:SettingsPage}"
        Route="SettingsPage" />

<Shell.FlyoutHeader>
    <Label Text="My Header" />
</Shell.FlyoutHeader>

`

With this code the first item "Home" can not be pressed. Removing Shell.FlyoutHeader the first item is pressable again.

gianisimone avatar Jan 09 '24 17:01 gianisimone

This is from what i found an issue since xamarin forms. And trying to fix this in impossible outside of the implementation in maui. A lot of internal classes. Main reason for this is a fix Margin down the way.

Workaround: <Shell.FlyoutHeader> <Frame Margin="0" BackgroundColor="Transparent" BorderColor="Transparent" > <HorizontalStackLayout Padding="8,12,8,0"> <Image Source="test.png" HeightRequest="24" MaximumWidthRequest="101" Aspect="AspectFit"/> </HorizontalStackLayout> </Frame> <Shell.FlyoutHeader>

Use a Frame with Margin="0" ;-)

Larhei avatar Jan 11 '24 16:01 Larhei

Having the same issue. As soon as a header is available, the first flyoutItem is no longer clickable on iOS.

The above mentioned workaround indeed works, BUT destroys the layout of the header if you have a VerticalStackLayout in it.

andreas-spindler-mw avatar Jan 11 '24 16:01 andreas-spindler-mw

@nsood9 Something like this. You can place whatever you'd like in <Shell.FlyoutContent>

AppShell.xaml

<Shell
    x:Class="MyMAUI.AppShell"
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:components="clr-namespace:MyMAUI.Components"
    xmlns:ios="clr-namespace:Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific;assembly=Microsoft.Maui.Controls"
    xmlns:startup="clr-namespace:MyMAUI.Views.Startup"
    Padding="0,0,0,0"
    ios:Page.UseSafeArea="True"
    FlyoutBackdrop="{StaticResource Tint}">

    <!-- Some <ShellContent>s all with FlyoutItemIsVisible="False"-->

    <Shell.FlyoutContent>
        <components:FlyoutContentComponent/>
    </Shell.FlyoutContent>
</Shell>

FlyoutContentComponent.xaml

<Border
    x:Class="MyMAUI.Components.FlyoutContentComponent"
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:components="clr-namespace:MyMAUI.components"
    x:Name="FlyoutContentBorder"
    Margin="0,0,0,0"
    Padding="0"
    x:DataType="components:FlyoutContentComponent"
    Background="White"
    InputTransparent="False"
    StrokeThickness="0"
    VerticalOptions="Fill">
    <Grid x:Name="FlyoutGrid" RowDefinitions="300, *, 60">
        <Border
            x:Name="FlyoutHeader"
            Grid.Row="0"
            ZIndex="2">
        <!-- Content in here is the header -->
        </Border>
        <ScrollView
            x:Name="FlyoutContent"
            Grid.Row="1"
            ZIndex="1">
        <!-- I have nav menu here -->
        </ScrollView>
        <Border
            x:Name="FlyoutFooter"
            Grid.Row="2"
            ZIndex="2">
         <!-- Content in here is the footer-->
         </Border>
    </Grid>
</Border>

jeff-eats-pubsubs avatar Jan 12 '24 16:01 jeff-eats-pubsubs

We have the same problem. As soon as we add a Shell.Header, the first item is not clickable.

It used to work with .NET 7 without problems (if that helps anything) and became a problem with .NET 8.

MSicc avatar Jan 19 '24 13:01 MSicc

I ran into the same issue. I took a similar approach as @Larhei, but instead of using a Frame, I used a Border.

My original code looked like this:

    <Shell.FlyoutHeader>
            <VerticalStackLayout BackgroundColor="{AppThemeBinding Light={StaticResource Primary},
                                              Dark={StaticResource Secondary}}"
                                              Padding="24">
                <Image
                    HeightRequest="120"
                    Source="logo.png"
                    WidthRequest="120" />
                <Label
                    FontSize="24"
                    HorizontalOptions="Center"
                    Text="{x:Static strings:AppResources.AppName}"
                    TextColor="{AppThemeBinding Light={StaticResource PrimaryLightLightest}}" />
            </VerticalStackLayout>
    </Shell.FlyoutHeader>

I changed it to this

    <Shell.FlyoutHeader>
        <Border
            BackgroundColor="{AppThemeBinding Light={StaticResource Primary},
                                              Dark={StaticResource Secondary}}"
            Margin="0"
            Padding="24"
            StrokeThickness="0">
            <VerticalStackLayout Margin="10">
                <Image
                    HeightRequest="120"
                    Source="logo.png"
                    WidthRequest="120" />
                <Label
                    FontSize="24"
                    HorizontalOptions="Center"
                    Text="{x:Static strings:AppResources.AppName}"
                    TextColor="{AppThemeBinding Light={StaticResource PrimaryLightLightest}}" />
            </VerticalStackLayout>
        </Border>
    </Shell.FlyoutHeader>

So the changes I made were: I wrapped the Headers VerticalStackLayout into a Border-Element. I moved the BackgroundColor from my VerticalStackLayout to the Border-Element and did the same for thePadding Property. I set the StrokeThicknes-Property to 0 to prevent a one pixel border appearing around my header. In addition to that I had to set a Margin of 10 to my VerticalStackLayout.

If you don't want to wrap your header in a border, you can just give your VerticalStackLayout or Grid or whatever you're displaying in your header a margin-top and margin-bottom of 36 (like this: margin="0,36"), which is the height of the ios safearea on most modern iOS devices. Although this works, I think the other option looks better because you don't get any white space in the safearea with the border approach.

I hope this helps anybody who runs into the same problem.

AndreKraemer avatar Jan 23 '24 21:01 AndreKraemer