Skip to content

[Android] Fix Shell singleton page rendering blank when re-pushed after absolute route reset - #36903

Merged
kubaflo merged 2 commits into
dotnet:inflight/currentfrom
HarishwaranVijayakumar:fix-36853
Jul 31, 2026
Merged

[Android] Fix Shell singleton page rendering blank when re-pushed after absolute route reset#36903
kubaflo merged 2 commits into
dotnet:inflight/currentfrom
HarishwaranVijayakumar:fix-36853

Conversation

@HarishwaranVijayakumar

@HarishwaranVijayakumar HarishwaranVijayakumar commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Note

Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!

Issue Details:

  • A DI-singleton page in Shell navigation renders blank when re-pushed after a GoToAsync("///MainPage") stack reset.

Root Cause of the issue

  • PR #35476 moved page.DisconnectHandlers() into RemovePage() , which runs on all pop paths including PopToRoot. This permanently destroys the handler for singleton pages. On Android, a pre-existing bug in RemoveAllPushedPages compounds this — it checks shellSection.Stack.Count which is already 1 by the time the platform event fires, so it early-returns and never removes stale fragments from _fragmentMap . On re-push, the old fragment is reused without calling OnCreateView , resulting in a blank page since the handler is null.

Why It Only Affects Singletons

  • Transient pages get a new instance each push → new fragment → renders fine. Singleton pages reuse the same instance → old stale fragment found → no OnCreateView → blank.

Description of Change

Bug Fix: Fragment Removal Logic

  • Improved the logic in RemoveAllPushedPages in ShellItemRendererBase.cs to ensure that fragment transactions are only committed if any fragments were actually removed, preventing issues where singleton pages could render blank after navigation.

New Test Case for Regression

  • Added a new test page and scenario in Issue36853.cs (HostApp) to reproduce the bug: navigating Root → SecondPage (singleton) → ThirdPage → PopToRoot → re-push SecondPage, which previously resulted in a blank screen.
  • Registered Issue36853SecondPage as a singleton and Issue36853ThirdPage as transient in the DI container, matching the real-world bug scenario. [1] [2]

Automated UITest

  • Introduced an automated UITest in TestCases.Shared.Tests/Tests/Issues/Issue36853.cs to verify that after PopToRoot and re-pushing the singleton page, its content is visible and not blank.

Issues Fixed

Fixes #36853
Fixes #36852

Tested the behavior in the following platforms

  • Windows
  • Android
  • iOS
  • Mac

Output

Before After
Beforefix-36853.mov
Afterfix-36853.mov

@github-actions

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 36903

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 36903"

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service dotnet-policy-service Bot added the community ✨ Community Contribution label Jul 29, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Hey there @@HarishwaranVijayakumar! Thank you so much for your PR! Someone from the team will get assigned to your PR shortly and we'll get it reviewed.

@dotnet-policy-service dotnet-policy-service Bot added the partner/syncfusion Issues / PR's with Syncfusion collaboration label Jul 29, 2026
@HarishwaranVijayakumar HarishwaranVijayakumar changed the title [WIP] [Android] Fix Shell singleton page rendering blank when re-pushed after absolute route reset [Android] Fix Shell singleton page rendering blank when re-pushed after absolute route reset Jul 30, 2026
@vishnumenon2684

Copy link
Copy Markdown
Contributor

/azp run maui-pr-uitests , maui-pr-devicetests

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 2 pipeline(s).

@sheiksyedm
sheiksyedm marked this pull request as ready for review July 30, 2026 08:03
Copilot AI review requested due to automatic review settings July 30, 2026 08:03
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes an Android Shell regression where DI-singleton pages can render blank when re-pushed after an absolute-route stack reset, by ensuring stale fragments are actually removed and fragment transactions are only committed when needed.

Changes:

  • Adjusted Android ShellItemRendererBase.RemoveAllPushedPages to lazily create/commit fragment transactions only when fragments are removed.
  • Added a HostApp repro scenario that uses DI singleton + transient pages with Shell routes to match the regression conditions.
  • Added an Appium UITest that validates the singleton page renders after /// reset and re-push.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellItemRendererBase.cs Avoids committing an empty fragment transaction and ensures pushed-page fragments are removed from the map/FragmentManager.
src/Controls/tests/TestCases.HostApp/MauiProgram.cs Registers DI services needed to reproduce the singleton/transient routing scenario.
src/Controls/tests/TestCases.HostApp/Issues/Issue36853.cs Adds the Shell-based repro page and registers routes + DI lifetimes matching the regression.
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue36853.cs Adds an automated regression UITest covering PopToRoot via absolute route and re-push behavior.


_fragmentMap.Remove(kvp.Key);

if (keepCurrent && kvp.Value.Fragment == _currentFragment)
@kubaflo

This comment has been minimized.

@github-actions github-actions Bot added the s/agent-review-in-progress AI review is currently running for this PR label Jul 30, 2026
@MauiBot MauiBot added s/agent-changes-requested AI agent recommends changes - found a better alternative or issues s/agent-gate-passed AI verified tests catch the bug (fail without fix, pass with fix) s/agent-fix-pr-picked AI could not beat the PR fix - PR is the best among all candidates s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review) labels Jul 30, 2026
@MauiBot

MauiBot commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

AI Review Summary

@HarishwaranVijayakumar — new AI review results are available based on this last commit: 3625949.

Gate Passed Confidence Low Platform Android


🗂️ Review Sessions — click to expand
🚦 Gate — Test Before & After Fix

Gate Result: ✅ PASSED

Platform: ANDROID · Base: main · Merge base: 62b927c2

Test Without Fix (expect FAIL) With Fix (expect PASS)
🖥️ Issue36853 Issue36853 ✅ FAIL — 2448s ✅ PASS — 582s
🔴 Without fix — 🖥️ Issue36853: FAIL ✅ · 2448s

Error-relevant lines (filtered from the build log):

/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010:    at Mono.AndroidTools.Internal.AdbOutputParsing.CheckInstallSuccess(String output, String packageName) [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010:    at Mono.AndroidTools.AndroidDevice.<>c__DisplayClass105_0.<InstallPackage>b__0(Task`1 t) [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010:    at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010: --- End of stack trace from previous location --- [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010:    at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread) [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010:    at AndroidDeviceExtensions.PushAndInstallPackageAsync(AndroidDevice device, PushAndInstallCommand command, CancellationToken token) [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010:    at Xamarin.Android.Tasks.FastDeploy.InstallPackage(Boolean installed) [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
/home/vsts/work/1/s/.dotnet/packs/Microsoft.Android.Sdk.Linux/36.1.2/tools/Xamarin.Android.Common.Debugging.targets(333,5): error ADB0010:    at Xamarin.Android.Tasks.FastDeploy.RunInstall() [/home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj::TargetFramework=net10.0-android]
Build FAILED.
     at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
   at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
   at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 793
   at Microsoft.Maui.TestCases.Tests.Issues.Issue36853.SingletonPageShouldRenderAfterPopToRootAndRePush() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue36853.cs:line 40
   at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
   at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
🟢 With fix — 🖥️ Issue36853: PASS ✅ · 582s

(no coded error found; showing last 1200 chars)

tion, please wait...
A total of 1 test files matched the specified pattern.
[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.8.2+699d445a1a (64-bit .NET 10.0.0)
[xUnit.net 00:00:00.12]   Discovering: Controls.TestCases.Android.Tests
[xUnit.net 00:00:00.36]   Discovered:  Controls.TestCases.Android.Tests
NUnit Adapter 4.5.0.0: Test execution started
Running selected tests in /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll
   NUnit3TestExecutor discovered 1 of 1 NUnit test cases using Current Discovery mode, Non-Explicit run
>>>>> 07/30/2026 16:30:55 FixtureSetup for Issue36853(Android)
>>>>> 07/30/2026 16:30:57 SingletonPageShouldRenderAfterPopToRootAndRePush Start
>>>>> 07/30/2026 16:31:08 SingletonPageShouldRenderAfterPopToRootAndRePush Stop
  Passed SingletonPageShouldRenderAfterPopToRootAndRePush [10 s]
NUnit Adapter 4.5.0.0: Test execution complete
Results File: /home/vsts/work/1/s/CustomAgentLogsTmp/UITests/TestResults/Issue36853.trx
Test Run Successful.
Total tests: 1
     Passed: 1
 Total time: 25.4457 Seconds
>>> TRX_RESULT_FILE: /home/vsts/work/1/s/CustomAgentLogsTmp/UITests/TestResults/Issue36853.trx
📁 Fix files reverted (1 files)
  • src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellItemRendererBase.cs

📱 UI Tests — Shell

Detected UI test categories: Shell

Deep UI tests — 310 passed, 0 failed across 1 category on platform-pool agent (replaces in-process counts above).

🧪 UI Test Execution Results (deep, platform pool)

Category Tests Snapshot diffs
Shell 310/310 ✓
📎 Download drop-deep-uitests artifact (TRX + snapshot diffs)

📋 Pre-Flight — Context & Validation

Issue: #36853 - [regression/10.0.90] Shell: DI-Singleton page pushed via a registered route renders blank when re-pushed after an absolute-route stack reset
PR: #36903 - [Android] Fix Shell singleton page rendering blank when re-pushed after absolute route reset
Platforms Affected: Android
Files Changed: 1 implementation, 3 test

Key Findings

  • The issue reproduces when an Android Shell DI-singleton registered-route page is pushed, another page is pushed on top, the stack is reset via GoToAsync("///Issue36853Main"), and the singleton page is pushed again.
  • The PR changes Android compatibility Shell fragment cleanup in ShellItemRendererBase.RemoveAllPushedPages so stale pushed-page fragments are removed based on _fragmentMap contents rather than the already-collapsed logical ShellSection.Stack.Count.
  • The PR adds Android-only Shell UI regression coverage in the host app and shared UI tests for issue [regression/10.0.90] Shell: DI-Singleton page pushed via a registered route renders blank when re-pushed after an absolute-route stack reset #36853.
  • Gate result supplied by caller: tests fail without the fix and pass with the PR fix. Gate was not re-run.

Code Review Summary

Verdict: NEEDS_DISCUSSION
Confidence: low
Errors: 0 | Warnings: 0 | Suggestions: 0

Key code review findings:

  • ℹ No ❌ Errors or ⚠️ Warnings found in the changed code; expert reviewer returned [].
  • ℹ Blast radius is Android Shell fragment lifecycle during PopToRoot; no startup path or static/shared state changes.
  • ℹ Required-check status was undetermined/pending, so the code-review skill capped confidence and avoided LGTM.

Fix Candidates

# Source Approach Test Result Files Changed Notes
PR PR #36903 Remove stale pushed-page fragments from _fragmentMap/FragmentManager during Android Shell PopToRoot, using lazy fragment transaction creation instead of stack-count early return. ✅ PASSED (Gate) ShellItemRendererBase.cs, issue #36853 UI-test files Original PR

🔬 Code Review — Deep Analysis

Code Review — PR #36903

Independent Assessment

What this changes: Android Shell PopToRoot now removes pushed-page fragments from _fragmentMap even after ShellSection.Stack has already collapsed to root, and avoids committing an empty fragment transaction. It adds an Android Shell UITest for singleton route page re-push after /// reset.

Inferred motivation: Prevent stale hidden fragments from being reused for DI-singleton routed pages whose handlers were disconnected during stack reset.

Reconciliation with PR Narrative

Author claims: Fixes #36853 by removing stale Android Shell fragments during absolute-route PopToRoot; adds regression coverage.

Agreement/disagreement: The code matches that claim. The old stack-count early return could skip removal after the logical stack had already reached root; the new map-based removal targets the stale-fragment failure mode described.

Prior Review Reconciliation

No prior ❌ Error findings found. Prior surfaces checked: top-level reviews, inline review comments, and issue comments. One prior Copilot inline comment was a non-blocking clarity suggestion about comparing fragment wrapper instances rather than Fragment to IShellObservableFragment.

Blast Radius Assessment

  • Runs for all instances: No — only Android compatibility Shell navigation during PopToRoot.
  • Startup impact: No.
  • Static/shared state: No new static state. The test adds DI registrations in HostApp only.
  • Handler/platform scope: Android Shell fragment lifecycle; max confidence is medium before CI evidence.

CI Status

  • Required-check result: Undetermined via required-check command. gh pr checks 36903 --repo dotnet/maui --required failed because gh is unauthenticated.
  • Public check-run evidence: maui-pr completed successfully, but Build Analysis was still in progress and maui-pr-devicetests / maui-pr-uitests umbrella checks were queued; many matrix jobs showed mixed success/cancelled.
  • Classification: Undetermined / pending.
  • Action taken: Confidence capped to low; per skill rules, no LGTM while CI is pending or required-check status is unavailable.

External Output Contract

Consumer token/pattern Producer location Producer emission condition Consumer assumption Ordinary negative case Downstream effect
N/A N/A No changed code classifies external tool output by regex/string token. N/A N/A N/A

Findings

No ❌ Errors or ⚠️ Warnings found in the changed code. Expert reviewer returned [].

Failure-Mode Probing

  • Root → singleton second → third → ///root → second: second page fragment is removed from _fragmentMap and FragmentManager before root is shown, so re-push creates a fresh fragment instead of showing a stale hidden one.
  • Root → second → ///root while second is current: current fragment is removed from the map, then removed by the main PopToRoot transaction; no stale map entry remains.
  • No pushed pages / empty removal: transaction is lazy and nullable, so no empty commit occurs.
  • Non-current tab PopToRoot: map entries for that section are still removed; this follows existing transaction behavior and avoids stale fragment reuse.
  • Handler reconnect: singleton page reuse should go through CreateFragmentForPage again after map removal, allowing OnCreateView/handler recreation.

Verdict: NEEDS_DISCUSSION

Confidence: low

Summary: I found no code-level correctness issues, and the fix matches the Android Shell stale-fragment failure mode. However, required CI status could not be determined with gh, and public checks still showed pending/queued work, so the skill rules prohibit LGTM until CI is complete/verified.


🛠️ Fix — Analysis & Comparison

Fix Candidates

# Source Approach Test Result Files Changed Notes
1 try-fix-1 Stale fragment detection at push time: when reusing a cached Android Shell fragment on Push, discard/recreate it if the fragment is no longer valid or the page handler is disconnected. ✅ PASS 1 file Passed after learning that fragment-added state alone was insufficient; page.Handler == null detected the singleton page reset case. Not selected yet because it adds push-path stale-state checks and may be broader than the PR's cleanup-at-source fix.
2 try-fix-2 Stack-diff reconciliation at the PopToRoot navigation-event boundary: leave RemoveAllPushedPages unchanged, then evict _fragmentMap page entries under the section that are no longer in ShellSection.Stack. ✅ PASS 1 file Passed the full Android UI repro. Not selected yet because it adds a second cleanup path and more code than the PR's direct fix.
3 try-fix-3 PopToRoot transition-time cleanup: prune pushed fragments for the current section inside the same PopToRoot fragment transaction before adding/showing the root target. ✅ PASS 1 file Passed the regression test, but is closer to the PR's sweep concept and only runs in the current-tab transaction path, making it less robust than the PR fix.
4 try-fix-4 Page-detach-driven fragment invalidation: subscribe mapped pages to ParentChanging and remove/dispose non-current fragments when a mapped page leaves its ShellSection. ✅ PASS 1 file Passed the regression test, but adds event subscription lifecycle risk and broader detach behavior for all pushed page mappings.
PR PR #36903 Remove stale pushed-page fragments from _fragmentMap/FragmentManager during Android Shell PopToRoot, using lazy fragment transaction creation instead of stack-count early return. ✅ PASSED (Gate) 4 files Original PR

Cross-Pollination

Model Round New Ideas? Details
claude-opus-4.6 1 Yes Candidate 1 generated and tested.
claude-opus-4.7 1 Yes Candidate 2 generated and tested.
gpt-5.3-codex 1 Yes Candidate 3 generated and tested.
gpt-5.5 1 Yes Candidate 4 generated and tested.
claude-opus-4.6 2 No NO NEW IDEAS: existing fixes cover stale fragment resurrection, stack-map reconciliation, PopToRoot transaction cleanup, and parent-detach invalidation.
claude-opus-4.7 2 No NO NEW IDEAS: four candidates cover push-time, pop-time, transaction-time, and parent-change-time intervention points.
gpt-5.3-codex 2 No NO NEW IDEAS: remaining options are timing/bookkeeping variations; PR fix is simpler and targeted.
gpt-5.5 2 No NO NEW IDEAS: handler-disconnect hooks, fragment-tag versioning, or weak-reference maps reduce to the same detection/eviction strategies and are not plausibly better than the PR's direct sweep.

Exhausted: Yes
Selected Fix: PR #36903 — all four alternative candidates passed the Android Issue36853 regression, but none is demonstrably better than the PR fix. Candidate 1 is broader because it guards every push and depends on handler-null staleness; candidate 2 duplicates cleanup responsibility with a larger reconciliation helper; candidate 3 is less robust for non-current tabs; candidate 4 adds event subscription lifecycle risk. The PR's direct RemoveAllPushedPages cleanup is the simplest targeted fix at the stale-map source.


🏁 Report — Final Recommendation

Comparative Fix Report — PR #36903

Summary

All evaluated candidates passed the supplied Android regression gate for Issue36853. Because candidates that fail regression tests must rank below passing candidates and none failed, the deciding factors are correctness, locality, lifecycle risk, and whether the approach fixes the stale fragment map at the root cause.

Candidate Ranking

Rank Candidate Regression Result Assessment
1 pr ✅ Passed Best choice. It removes stale pushed-page fragments from _fragmentMap during RemoveAllPushedPages, the cleanup point that previously skipped work because ShellSection.Stack was already collapsed. It is the smallest targeted production change and is covered by the new Android Shell UI regression test.
1 pr-plus-reviewer ✅ Passed Equivalent to pr. The expert reviewer returned [], so there was no actionable feedback to apply and no code delta from the PR fix.
3 try-fix-2 ✅ Passed Strong alternative. Reconciles _fragmentMap against ShellSection.Stack after PopToRoot, which is precise and defensible, but adds a second cleanup helper/path instead of fixing the existing cleanup method directly.
4 try-fix-3 ✅ Passed Cleans stale pushed fragments in the active PopToRoot transition transaction. It is localized, but narrower than the PR because it is tied to the current-tab transaction path rather than the general pushed-page cleanup helper.
5 try-fix-1 ✅ Passed Detects stale fragments lazily on push via fragment state and page.Handler == null. It works for the repro but broadens the push path and depends on handler-null staleness heuristics rather than preventing stale cache entries at cleanup time.
6 try-fix-4 ✅ Passed Cleans fragments when mapped pages leave their ShellSection. It works but adds event subscription/unsubscription lifecycle surface and broader behavior changes than needed for the PopToRoot stale-map bug.

Comparative Analysis

The PR fix is the most direct root-cause repair. The baseline bug is that RemoveAllPushedPages used ShellSection.Stack.Count as a guard, but absolute-route reset can collapse the logical stack before Android fragment cleanup runs. Removing that guard and sweeping _fragmentMap by Parent == shellSection lets the existing cleanup path do the work it was originally meant to do.

try-fix-2 is the closest competitor because it also repairs map consistency at the PopToRoot boundary and avoids push-time heuristics. However, it leaves the old early-returning helper in place and adds reconciliation beside it, which is more code and more maintenance surface than correcting RemoveAllPushedPages.

try-fix-1, try-fix-3, and try-fix-4 each passed the regression but intervene at less ideal points: push-time stale detection, current transition cleanup, or page-parent detachment. Those approaches either broaden behavior outside the failing cleanup path or introduce extra lifecycle complexity.

Winner

Winner: pr

The raw PR fix wins because it passed the regression gate, has no expert-reviewer findings, and is the simplest targeted fix at the stale fragment-map cleanup source. pr-plus-reviewer is functionally identical because no reviewer feedback was applied, so selecting pr preserves the submitted implementation without unnecessary churn.


🧭 Next Steps — review latest findings

No alternative fix was selected for this run. Review the session findings and CI results before merging.

@MauiBot MauiBot removed the s/agent-review-in-progress AI review is currently running for this PR label Jul 30, 2026
@kubaflo
kubaflo changed the base branch from main to inflight/current July 31, 2026 09:51
@kubaflo
kubaflo merged commit e0df1f6 into dotnet:inflight/current Jul 31, 2026
116 of 169 checks passed
@github-actions github-actions Bot added this to the .NET 10 SR10 milestone Jul 31, 2026
@kubaflo

kubaflo commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

/backport to inflight/candidate

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Started backporting to inflight/candidate (link to workflow run)

kubaflo pushed a commit that referenced this pull request Aug 5, 2026
…nk when re-pushed after absolute route reset (#37113)

Backport of #36903 to inflight/candidate

/cc @kubaflo @HarishwaranVijayakumar

Co-authored-by: HarishwaranVijayakumar <harishwaran.vijayakumar@syncfusion.com>
kubaflo pushed a commit that referenced this pull request Aug 7, 2026
…er absolute route reset (#36903)

<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!
<!--
!!!!!!! MAIN IS THE ONLY ACTIVE BRANCH. MAKE SURE THIS PR IS TARGETING
MAIN. !!!!!!!
-->

### Issue Details:

- A DI-singleton page in Shell navigation renders blank when re-pushed
after a GoToAsync("///MainPage") stack reset.

### Root Cause of the issue

- PR #[35476](#35476) moved
page.DisconnectHandlers() into RemovePage() , which runs on all pop
paths including PopToRoot. This permanently destroys the handler for
singleton pages. On Android, a pre-existing bug in RemoveAllPushedPages
compounds this — it checks shellSection.Stack.Count which is already 1
by the time the platform event fires, so it early-returns and never
removes stale fragments from _fragmentMap . On re-push, the old fragment
is reused without calling OnCreateView , resulting in a blank page since
the handler is null.

**Why It Only Affects Singletons**

- Transient pages get a new instance each push → new fragment → renders
fine. Singleton pages reuse the same instance → old stale fragment found
→ no OnCreateView → blank.

### Description of Change

### Bug Fix: Fragment Removal Logic

* Improved the logic in `RemoveAllPushedPages` in
`ShellItemRendererBase.cs` to ensure that fragment transactions are only
committed if any fragments were actually removed, preventing issues
where singleton pages could render blank after navigation.

### New Test Case for Regression

* Added a new test page and scenario in `Issue36853.cs` (HostApp) to
reproduce the bug: navigating Root → SecondPage (singleton) → ThirdPage
→ PopToRoot → re-push SecondPage, which previously resulted in a blank
screen.
* Registered `Issue36853SecondPage` as a singleton and
`Issue36853ThirdPage` as transient in the DI container, matching the
real-world bug scenario.
[[1]](diffhunk://#diff-70f90c4c40092b9b58b067b769ce0a5ad5db3b0a8a72359fd7b816a682ad181bR1-R111)
[[2]](diffhunk://#diff-d01e2392fa68bd429a87ca25ef57d4ad83147746e650291c69f97c197d92628dL44-R45)

### Automated UITest

* Introduced an automated UITest in
`TestCases.Shared.Tests/Tests/Issues/Issue36853.cs` to verify that after
PopToRoot and re-pushing the singleton page, its content is visible and
not blank.

### Issues Fixed

<!-- Please make sure that there is a bug logged for the issue being
fixed. The bug should describe the problem and how to reproduce it. -->

Fixes #36853 

### Tested the behavior in the following platforms
 
- [x] Windows
- [x] Android
- [x] iOS
- [x] Mac

### Output
 
| Before | After |
|----------|----------|
| <video
src="https://github.com/user-attachments/assets/bb4201ac-5be1-4553-8f34-2b4f4556dfd9">
| <video
src="https://github.com/user-attachments/assets/de6b0981-26d4-4c79-9490-9f1c65e17f92">
|








<!--
Are you targeting main? All PRs should target the main branch unless
otherwise noted.
-->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

community ✨ Community Contribution partner/syncfusion Issues / PR's with Syncfusion collaboration s/agent-changes-requested AI agent recommends changes - found a better alternative or issues s/agent-fix-pr-picked AI could not beat the PR fix - PR is the best among all candidates s/agent-gate-passed AI verified tests catch the bug (fail without fix, pass with fix) s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[regression/10.0.90] Shell: DI-Singleton page pushed via a registered route renders blank when re-pushed after an absolute-route stack reset

5 participants