diff --git a/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellItemRendererBase.cs b/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellItemRendererBase.cs index 63cce3dfdcfa..744a92309ef6 100644 --- a/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellItemRendererBase.cs +++ b/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellItemRendererBase.cs @@ -406,25 +406,27 @@ void RemoveAllButCurrent(Fragment skip) void RemoveAllPushedPages(ShellSection shellSection, bool keepCurrent) { - if (shellSection.Stack.Count <= 1 || (keepCurrent && shellSection.Stack.Count == 2)) - return; - - var t = ChildFragmentManager.BeginTransactionEx(); + FragmentTransaction t = null; foreach (var kvp in _fragmentMap.ToList()) { if (kvp.Key.Parent != shellSection) + { continue; + } _fragmentMap.Remove(kvp.Key); if (keepCurrent && kvp.Value.Fragment == _currentFragment) + { continue; + } + t ??= ChildFragmentManager.BeginTransactionEx(); t.RemoveEx(kvp.Value.Fragment); } - t.CommitAllowingStateLossEx(); + t?.CommitAllowingStateLossEx(); } void RemoveFragment(Fragment fragment) diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue36853.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue36853.cs new file mode 100644 index 000000000000..463f48e5cbc5 --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue36853.cs @@ -0,0 +1,111 @@ +namespace Maui.Controls.Sample.Issues; + +// Reproduces the bug: Root → SecondPage (singleton) → ThirdPage → ///Root → SecondPage again → BLANK +[Issue(IssueTracker.Github, 36853, "Shell singleton page renders blank when re-pushed after absolute route PopToRoot on Android", PlatformAffected.Android)] +public class Issue36853 : TestShell +{ + protected override void Init() + { + Routing.RegisterRoute("Issue36853Second", typeof(Issue36853SecondPage)); + Routing.RegisterRoute("Issue36853Third", typeof(Issue36853ThirdPage)); + + var mainPage = new ContentPage + { + Title = "Main", + Content = new VerticalStackLayout + { + Spacing = 20, + Padding = 20, + Children = + { + new Label + { + Text = "Main Page", + AutomationId = "Issue36853MainLabel", + FontSize = 24 + }, + new Button + { + Text = "Go to Second Page", + AutomationId = "Issue36853GoToSecond", + Command = new Command(async () => + await Shell.Current.GoToAsync("Issue36853Second")) + } + } + } + }; + + AddContentPage(mainPage, "Issue36853Main"); + } +} + +// Registered as Singleton in DI — same instance returned every time the route resolves +public class Issue36853SecondPage : ContentPage +{ + public Issue36853SecondPage() + { + Title = "Second"; + Content = new VerticalStackLayout + { + Spacing = 20, + Padding = 20, + Children = + { + new Label + { + Text = "Second Page Content", + AutomationId = "Issue36853SecondLabel", + FontSize = 24 + }, + new Button + { + Text = "Go to Third Page", + AutomationId = "Issue36853GoToThird", + Command = new Command(async () => + await Shell.Current.GoToAsync("Issue36853Third")) + } + } + }; + } +} + +public class Issue36853ThirdPage : ContentPage +{ + public Issue36853ThirdPage() + { + Title = "Third"; + Content = new VerticalStackLayout + { + Spacing = 20, + Padding = 20, + Children = + { + new Label + { + Text = "Third Page Content", + AutomationId = "Issue36853ThirdLabel", + FontSize = 24 + }, + new Button + { + Text = "Reset to Root (///)", + AutomationId = "Issue36853ResetToRoot", + Command = new Command(async () => + await Shell.Current.GoToAsync("///Issue36853Main")) + } + } + }; + } +} + +static class Issue36853Extensions +{ + public static MauiAppBuilder Issue36853RegisterServices(this MauiAppBuilder builder) + { + // SecondPage is singleton — same instance reused across navigations (the bug scenario) + builder.Services.AddSingleton(); + // ThirdPage is transient — new instance each time (normal behavior) + builder.Services.AddTransient(); + return builder; + } +} diff --git a/src/Controls/tests/TestCases.HostApp/MauiProgram.cs b/src/Controls/tests/TestCases.HostApp/MauiProgram.cs index beae20e666d9..f0b7fc221cc6 100644 --- a/src/Controls/tests/TestCases.HostApp/MauiProgram.cs +++ b/src/Controls/tests/TestCases.HostApp/MauiProgram.cs @@ -41,7 +41,8 @@ public static MauiApp CreateMauiApp() .Issue18720DatePickerAddMappers() .Issue18720TimePickerAddMappers() .Issue28945AddMappers() - .Issue25436RegisterNavigationService(); + .Issue25436RegisterNavigationService() + .Issue36853RegisterServices(); #if IOS || MACCATALYST appBuilder.ConfigureCollectionViewHandlers(); diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue36853.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue36853.cs new file mode 100644 index 000000000000..9c3efd79944d --- /dev/null +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue36853.cs @@ -0,0 +1,42 @@ +using NUnit.Framework; +using UITest.Appium; +using UITest.Core; + +namespace Microsoft.Maui.TestCases.Tests.Issues; + +public class Issue36853 : _IssuesUITest +{ + public override string Issue => "Shell singleton page renders blank when re-pushed after absolute route PopToRoot on Android"; + + public Issue36853(TestDevice testDevice) : base(testDevice) + { + } + + [Test] + [Category(UITestCategories.Shell)] + public void SingletonPageShouldRenderAfterPopToRootAndRePush() + { + // Step 1: From root, push SecondPage (singleton) + App.WaitForElement("Issue36853GoToSecond"); + App.Tap("Issue36853GoToSecond"); + + // Step 2: Verify SecondPage renders + App.WaitForElement("Issue36853SecondLabel"); + + // Step 3: Push ThirdPage on top (so stack is Root → Second → Third) + App.Tap("Issue36853GoToThird"); + App.WaitForElement("Issue36853ThirdLabel"); + + // Step 4: PopToRoot via absolute route /// + App.Tap("Issue36853ResetToRoot"); + App.WaitForElement("Issue36853MainLabel"); + + // Step 5: Re-push SecondPage (same singleton instance) + App.Tap("Issue36853GoToSecond"); + + // Step 6: SecondPage content must be visible — not blank + // Without the fix, WaitForElement will timeout because the stale fragment + // has a disconnected handler and OnCreateView never fires — page is blank. + App.WaitForElement("Issue36853SecondLabel"); + } +}