Skip to content

Fix [Android] Title of FlyOutPage is not updating anymore after showing a NonFlyOutPage#34839

Merged
kubaflo merged 5 commits into
dotnet:inflight/currentfrom
KarthikRajaKalaimani:fix-33615
Apr 16, 2026
Merged

Fix [Android] Title of FlyOutPage is not updating anymore after showing a NonFlyOutPage#34839
kubaflo merged 5 commits into
dotnet:inflight/currentfrom
KarthikRajaKalaimani:fix-33615

Conversation

@KarthikRajaKalaimani
Copy link
Copy Markdown
Contributor

@KarthikRajaKalaimani KarthikRajaKalaimani commented Apr 7, 2026

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:

Title of FlyOutPage is not updating anymore after showing a NonFlyOutPage in Android Platform.

Root Cause:

When Window.Page is swapped from a FlyoutPage to a plain ContentPage, the Window.Page setter clears flyoutPage.Parent = null synchronously before propagating the Window=null event through the element hierarchy. This means by the time NavigationPage.OnWindowChanged(null) fires to clean up the toolbar, the check flyoutPage.Parent is IWindow evaluates to false (Parent is already null). As a result, flyoutPage.Toolbar = null never executes — the stale, now-disconnected NavigationPageToolbar remains attached to
FlyoutPage.Toolbar. When FlyoutPage is later restored as Window.Page, FindMyToolbar() traverses the ancestors, finds the stale toolbar on FlyoutPage.Toolbar, and returns early — no new connected toolbar is created. Since the stale toolbar's ToolbarTracker subscriptions were severed by Disconnect(), it never receives CurrentPage change notifications, so the title is permanently frozen.

Description of Change:

Remove the flyoutPage.Parent is IWindow && condition from the guard in NavigationPage.OnWindowChanged(null) in NavigationPage.cs. The remaining check flyoutPage.Toolbar == _toolbar is the correct and sufficient invariant — it ensures we only clear the toolbar that this NavigationPage created. The Parent is IWindow guard was redundant in the normal case (when FlyoutPage is the root page, Parent is IWindow), but fatally incorrect during a Window.Page swap because the ordering guarantee it depended on didn't hold.

Tested the behavior in the following platforms:

  • Android
  • Windows
  • iOS
  • Mac

Reference:

N/A

Issues Fixed:

Fixes #33615

Screenshots

Before After
33615_Before.mov
33615_After.mov

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 7, 2026

🚀 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 -- 34839

Or

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

@dotnet-policy-service dotnet-policy-service Bot added community ✨ Community Contribution partner/syncfusion Issues / PR's with Syncfusion collaboration labels Apr 7, 2026
@sheiksyedm
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).

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes a toolbar lifecycle edge-case when swapping Window.Page away from (and back to) a FlyoutPage, which could leave a stale/disconnected NavigationPageToolbar attached and prevent subsequent title updates. Adds a new UI test and corresponding visual baselines to validate the title updates after returning from a non-flyout root page.

Changes:

  • Update NavigationPage.OnWindowChanged(null) to clear the FlyoutPage.Toolbar created by the current NavigationPage even when flyoutPage.Parent is already null.
  • Add new HostApp issue page + Appium UI test for Issue #33615 validating the title update scenario.
  • Add visual baseline images for the new screenshot-based UI test.

Reviewed changes

Copilot reviewed 3 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/Controls/src/Core/NavigationPage/NavigationPage.cs Removes an overly-strict guard so the stale toolbar is correctly cleared during Window.Page swaps.
src/Controls/tests/TestCases.HostApp/Issues/Issue33615.cs Adds a repro page that swaps Window.Page to a non-flyout page and back, then navigates to a second detail page.
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33615.cs Adds an Appium UI test that verifies the title updates via VerifyScreenshot().
src/Controls/tests/TestCases.Android.Tests/snapshots/android/TitleUpdatesAfterShowingNonFlyoutPage.png Android baseline image for the new visual assertion.
src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/TitleUpdatesAfterShowingNonFlyoutPage.png iOS baseline image for the new visual assertion.
src/Controls/tests/TestCases.WinUI.Tests/snapshots/windows/TitleUpdatesAfterShowingNonFlyoutPage.png Windows baseline image for the new visual assertion.

@MauiBot
Copy link
Copy Markdown
Collaborator

MauiBot commented Apr 15, 2026

🚦 Gate — Test Before and After Fix

👋 @KarthikRajaKalaimani — new gate results are available. Please review the latest session below.

🚦 Gate Sessione0fb74a · Snapshot added for winui and mac platforms · 2026-04-15 20:45 UTC

Gate Result: ⚠️ ENV ERROR

Platform: ANDROID · Base: main · Merge base: eb0b82fe

Test Without Fix (expect FAIL) With Fix (expect PASS)
🖥️ Issue33615 Issue33615 ⚠️ ENV ERROR ✅ PASS — 557s
🔴 Without fix — 🖥️ Issue33615: ⚠️ ENV ERROR · 1914s

(truncated to last 15,000 chars)

n 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.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]
    0 Warning(s)
    1 Error(s)

Time Elapsed 00:18:12.53
* daemon not running; starting now at tcp:5037
* daemon started successfully
  Determining projects to restore...
  All projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0-android36.0/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0-android36.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  Core -> /home/vsts/work/1/s/artifacts/bin/Core/Debug/net10.0-android36.0/Microsoft.Maui.dll
  Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  Maps -> /home/vsts/work/1/s/artifacts/bin/Maps/Debug/net10.0-android36.0/Microsoft.Maui.Maps.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.Core/Debug/net10.0-android36.0/Microsoft.Maui.Controls.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  Controls.Foldable -> /home/vsts/work/1/s/artifacts/bin/Controls.Foldable/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Foldable.dll
  Controls.Xaml -> /home/vsts/work/1/s/artifacts/bin/Controls.Xaml/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Xaml.dll
  Microsoft.AspNetCore.Components.WebView.Maui -> /home/vsts/work/1/s/artifacts/bin/Microsoft.AspNetCore.Components.WebView.Maui/Debug/net10.0-android36.0/Microsoft.AspNetCore.Components.WebView.Maui.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  Controls.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.Maps/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Maps.dll
  Controls.TestCases.HostApp -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Controls.TestCases.HostApp.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  Graphics -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  Core -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.dll
  Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Maps.dll
  Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  Microsoft.AspNetCore.Components.WebView.Maui -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.AspNetCore.Components.WebView.Maui.dll
  Controls.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Maps.dll
  Controls.Xaml -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Xaml.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  Controls.Foldable -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Foldable.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:09:28.54
Broadcasting: Intent { act=android.intent.action.CLOSE_SYSTEM_DIALOGS flg=0x400000 }
Broadcast completed: result=0
Broadcasting: Intent { act=android.intent.action.CLOSE_SYSTEM_DIALOGS flg=0x400000 }
Broadcast completed: result=0
  Determining projects to restore...
  Restored /home/vsts/work/1/s/src/TestUtils/src/VisualTestUtils/VisualTestUtils.csproj (in 1.47 sec).
  Restored /home/vsts/work/1/s/src/TestUtils/src/UITest.NUnit/UITest.NUnit.csproj (in 1.33 sec).
  Restored /home/vsts/work/1/s/src/TestUtils/src/UITest.Core/UITest.Core.csproj (in 6 ms).
  Restored /home/vsts/work/1/s/src/TestUtils/src/UITest.Appium/UITest.Appium.csproj (in 1.2 sec).
  Restored /home/vsts/work/1/s/src/TestUtils/src/VisualTestUtils.MagickNet/VisualTestUtils.MagickNet.csproj (in 4.58 sec).
  Restored /home/vsts/work/1/s/src/Controls/tests/TestCases.Android.Tests/Controls.TestCases.Android.Tests.csproj (in 1.51 sec).
  Restored /home/vsts/work/1/s/src/Controls/tests/CustomAttributes/Controls.CustomAttributes.csproj (in 4 ms).
  Restored /home/vsts/work/1/s/src/TestUtils/src/UITest.Analyzers/UITest.Analyzers.csproj (in 3.09 sec).
  5 of 13 projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0/Microsoft.Maui.Graphics.dll
  Controls.CustomAttributes -> /home/vsts/work/1/s/artifacts/bin/Controls.CustomAttributes/Debug/net10.0/Controls.CustomAttributes.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  Core -> /home/vsts/work/1/s/artifacts/bin/Core/Debug/net10.0/Microsoft.Maui.dll
  Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.Core/Debug/net10.0/Microsoft.Maui.Controls.dll
  VisualTestUtils -> /home/vsts/work/1/s/artifacts/bin/VisualTestUtils/Debug/netstandard2.0/VisualTestUtils.dll
  UITest.Core -> /home/vsts/work/1/s/artifacts/bin/UITest.Core/Debug/net10.0/UITest.Core.dll
  UITest.Appium -> /home/vsts/work/1/s/artifacts/bin/UITest.Appium/Debug/net10.0/UITest.Appium.dll
  UITest.NUnit -> /home/vsts/work/1/s/artifacts/bin/UITest.NUnit/Debug/net10.0/UITest.NUnit.dll
  VisualTestUtils.MagickNet -> /home/vsts/work/1/s/artifacts/bin/VisualTestUtils.MagickNet/Debug/netstandard2.0/VisualTestUtils.MagickNet.dll
  UITest.Analyzers -> /home/vsts/work/1/s/artifacts/bin/UITest.Analyzers/Debug/netstandard2.0/UITest.Analyzers.dll
  Controls.TestCases.Android.Tests -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll
Test run for /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll (.NETCoreApp,Version=v10.0)
VSTest version 18.0.1 (x64)

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
/home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll
[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.51]   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
>>>>> 04/15/2026 20:34:49 FixtureSetup for Issue33615(Android)
>>>>> 04/15/2026 20:35:05 The FixtureSetup threw an exception. Attempt 0/1.
Exception details: System.TimeoutException: [Android] Title of FlyOutPage is not updating anymore after showing a NonFlyOutPage
   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._IssuesUITest.NavigateToIssue(String issue) in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/_IssuesUITest.cs:line 54
   at Microsoft.Maui.TestCases.Tests._IssuesUITest.TryToResetTestState() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/_IssuesUITest.cs:line 25
   at Microsoft.Maui.TestCases.Tests.UITest.FixtureSetup() in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 576
>>>>> 04/15/2026 20:35:08 FixtureSetup for Issue33615(Android)
>>>>> 04/15/2026 20:35:24 The FixtureSetup threw an exception. Attempt 1/1.
Exception details: System.TimeoutException: [Android] Title of FlyOutPage is not updating anymore after showing a NonFlyOutPage
   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._IssuesUITest.NavigateToIssue(String issue) in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/_IssuesUITest.cs:line 54
   at Microsoft.Maui.TestCases.Tests._IssuesUITest.TryToResetTestState() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/_IssuesUITest.cs:line 25
   at Microsoft.Maui.TestCases.Tests.UITest.FixtureSetup() in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 576
>>>>> 04/15/2026 20:35:24 Log types: logcat, bugreport, server
>>>>> 04/15/2026 20:35:25 Log types: logcat, bugreport, server
  Failed TitleUpdatesAfterShowingNonFlyoutPage [51 s]
  Error Message:
   OneTimeSetUp: System.TimeoutException : [Android] Title of FlyOutPage is not updating anymore after showing a NonFlyOutPage
  Stack Trace:
     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._IssuesUITest.NavigateToIssue(String issue) in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/_IssuesUITest.cs:line 54
   at Microsoft.Maui.TestCases.Tests._IssuesUITest.TryToResetTestState() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/_IssuesUITest.cs:line 25
   at Microsoft.Maui.TestCases.Tests.UITest.FixtureSetup() in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 576
   at UITest.Appium.NUnit.UITestBase.OneTimeSetup() in /_/src/TestUtils/src/UITest.NUnit/UITestBase.cs:line 221
   at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
   at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)

Setup failed for test fixture Microsoft.Maui.TestCases.Tests.Issues.Issue33615(Android)
System.TimeoutException : [Android] Title of FlyOutPage is not updating anymore after showing a NonFlyOutPage
StackTrace:    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._IssuesUITest.NavigateToIssue(String issue) in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/_IssuesUITest.cs:line 54
   at Microsoft.Maui.TestCases.Tests._IssuesUITest.TryToResetTestState() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/_IssuesUITest.cs:line 25
   at Microsoft.Maui.TestCases.Tests.UITest.FixtureSetup() in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 576
   at UITest.Appium.NUnit.UITestBase.OneTimeSetup() in /_/src/TestUtils/src/UITest.NUnit/UITestBase.cs:line 221
   at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
   at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
NUnit Adapter 4.5.0.0: Test execution complete

Test Run Failed.
Total tests: 1
     Failed: 1
 Total time: 1.0389 Minutes

🟢 With fix — 🖥️ Issue33615: PASS ✅ · 557s
  Determining projects to restore...
  All projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0-android36.0/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0-android36.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  Core -> /home/vsts/work/1/s/artifacts/bin/Core/Debug/net10.0-android36.0/Microsoft.Maui.dll
  Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  Maps -> /home/vsts/work/1/s/artifacts/bin/Maps/Debug/net10.0-android36.0/Microsoft.Maui.Maps.dll
  Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.Core/Debug/net10.0-android36.0/Microsoft.Maui.Controls.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  Controls.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.Maps/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Maps.dll
  Controls.Foldable -> /home/vsts/work/1/s/artifacts/bin/Controls.Foldable/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Foldable.dll
  Controls.Xaml -> /home/vsts/work/1/s/artifacts/bin/Controls.Xaml/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Xaml.dll
  Microsoft.AspNetCore.Components.WebView.Maui -> /home/vsts/work/1/s/artifacts/bin/Microsoft.AspNetCore.Components.WebView.Maui/Debug/net10.0-android36.0/Microsoft.AspNetCore.Components.WebView.Maui.dll
  Controls.TestCases.HostApp -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Controls.TestCases.HostApp.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  Graphics -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  Core -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Maps.dll
  Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  Controls.Foldable -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Foldable.dll
  Microsoft.AspNetCore.Components.WebView.Maui -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.AspNetCore.Components.WebView.Maui.dll
  Controls.Xaml -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Xaml.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  Controls.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Maps.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:07:10.42
Broadcasting: Intent { act=android.intent.action.CLOSE_SYSTEM_DIALOGS flg=0x400000 }
Broadcast completed: result=0
Broadcasting: Intent { act=android.intent.action.CLOSE_SYSTEM_DIALOGS flg=0x400000 }
Broadcast completed: result=0
  Determining projects to restore...
  All projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0/Microsoft.Maui.Essentials.dll
  Controls.CustomAttributes -> /home/vsts/work/1/s/artifacts/bin/Controls.CustomAttributes/Debug/net10.0/Controls.CustomAttributes.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  Core -> /home/vsts/work/1/s/artifacts/bin/Core/Debug/net10.0/Microsoft.Maui.dll
  Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.70-ci+azdo.13847620
  Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.Core/Debug/net10.0/Microsoft.Maui.Controls.dll
  VisualTestUtils -> /home/vsts/work/1/s/artifacts/bin/VisualTestUtils/Debug/netstandard2.0/VisualTestUtils.dll
  UITest.Core -> /home/vsts/work/1/s/artifacts/bin/UITest.Core/Debug/net10.0/UITest.Core.dll
  UITest.Appium -> /home/vsts/work/1/s/artifacts/bin/UITest.Appium/Debug/net10.0/UITest.Appium.dll
  UITest.NUnit -> /home/vsts/work/1/s/artifacts/bin/UITest.NUnit/Debug/net10.0/UITest.NUnit.dll
  VisualTestUtils.MagickNet -> /home/vsts/work/1/s/artifacts/bin/VisualTestUtils.MagickNet/Debug/netstandard2.0/VisualTestUtils.MagickNet.dll
  UITest.Analyzers -> /home/vsts/work/1/s/artifacts/bin/UITest.Analyzers/Debug/netstandard2.0/UITest.Analyzers.dll
  Controls.TestCases.Android.Tests -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll
Test run for /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll (.NETCoreApp,Version=v10.0)
VSTest version 18.0.1 (x64)

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
/home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll
[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.11]   Discovering: Controls.TestCases.Android.Tests
[xUnit.net 00:00:00.44]   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
>>>>> 04/15/2026 20:45:04 FixtureSetup for Issue33615(Android)
>>>>> 04/15/2026 20:45:07 TitleUpdatesAfterShowingNonFlyoutPage Start
>>>>> 04/15/2026 20:45:14 TitleUpdatesAfterShowingNonFlyoutPage Stop
  Passed TitleUpdatesAfterShowingNonFlyoutPage [7 s]
NUnit Adapter 4.5.0.0: Test execution complete

Test Run Successful.
Total tests: 1
     Passed: 1
 Total time: 22.3810 Seconds

⚠️ Issues found
  • ⚠️ Issue33615 without fix: Exception calling "Matches" with "2" argument(s): "Value cannot be null. (Parameter 'input')"
📁 Fix files reverted (1 files)
  • src/Controls/src/Core/NavigationPage/NavigationPage.cs

@MauiBot
Copy link
Copy Markdown
Collaborator

MauiBot commented Apr 15, 2026

🤖 AI Summary

👋 @KarthikRajaKalaimani — new AI review results are available. Please review the latest session below.

📊 Review Sessione0fb74a · Snapshot added for winui and mac platforms · 2026-04-15 21:38 UTC
🔍 Pre-Flight — Context & Validation

Issue: #33615 - [Android] Title of FlyOutPage is not updating anymore after showing a NonFlyOutPage
PR: #34839 - Fix [Android] Title of FlyOutPage is not updating anymore after showing a NonFlyOutPage
Platforms Affected: Android (primarily), iOS, macOS, Windows (shared code path)
Files Changed: 1 implementation, 3 test (+ 5 snapshot images)

Key Findings

  • The bug is a race condition in NavigationPage.OnWindowChanged: when Window.Page is swapped from a FlyoutPage to a plain ContentPage, flyoutPage.Parent is nulled synchronously before the Window=null event fires through the hierarchy. The flyoutPage.Parent is IWindow guard in teardown therefore evaluates false, leaving a stale disconnected toolbar attached to FlyoutPage.Toolbar.
  • When FlyoutPage is later restored as Window.Page, FindMyToolbar() finds the stale toolbar and returns early — no new toolbar is created, title is frozen permanently.
  • Fix: Remove flyoutPage.Parent is IWindow && from the teardown guard. The remaining flyoutPage.Toolbar == _toolbar invariant is sufficient (only true when this NavigationPage was the one that set the toolbar).
  • Test covers the exact scenario from the issue: show NonFlyoutPage → return to FlyoutPage → navigate to DetailPage2 → verify title updates.
  • CI: 36/37 checks green. Only failure: RunOniOS_MauiNativeAOT ARM64 (~8 min fast-fail, unrelated to FlyoutPage).

Code Review Summary

Verdict: NEEDS_DISCUSSION
Confidence: high
Errors: 0 | Warnings: 1 | Suggestions: 2

Key code review findings:

  • ⚠️ maui-pr overall is red due to RunOniOS_MauiNativeAOT ARM64 failure — looks like infra flake (fast-fail, unrelated to FlyoutPage). Should be confirmed before merge.
  • 💡 VerifyScreenshot() called without retryTimeout after PushAsync navigation — toolbar title may lag on slow CI devices (Issue33615.cs:41)
  • 💡 PlatformAffected.Android on [Issue] attribute while test + snapshots cover all platforms — consider PlatformAffected.All (Issue33615.cs:4)

Fix Candidates

# Source Approach Test Result Files Changed Notes
PR PR #34839 Remove flyoutPage.Parent is IWindow && from teardown guard in OnWindowChanged ✅ PASSED (Gate) NavigationPage.cs Original PR

🔬 Code Review — Deep Analysis

Code Review — PR #34839

Independent Assessment

What this changes: A single-line condition change in NavigationPage.OnWindowChanged. When the NavigationPage is being removed from the window (this.Window is null), the teardown code attempts to clear flyoutPage.Toolbar. The old guard was:

flyoutPage != null && flyoutPage.Parent is IWindow && flyoutPage.Toolbar == _toolbar

The new guard drops flyoutPage.Parent is IWindow:

flyoutPage is not null && flyoutPage.Toolbar == _toolbar

The PR also adds a complete UI test for the scenario (HostApp page + NUnit test + snapshots for all 5 platform targets).

Inferred motivation: When Window.Page is swapped from a FlyoutPage to a plain ContentPage, the element tree is reorganized synchronously. By the time OnWindowChanged fires on the NavigationPage (to tear down its toolbar), flyoutPage.Parent has already been nulled out. The is IWindow check therefore returns false, and the stale toolbar is never cleared. When the FlyoutPage is later restored as Window.Page, FindMyToolbar() finds the stale, disconnected toolbar and returns early — so no new toolbar is ever created, and the title freezes.


Reconciliation with PR Narrative

Author claims: Exactly what the code shows. The root-cause explanation ("Window.Page setter clears flyoutPage.Parent = null synchronously before propagating the Window=null event") is precise and matches the code. The author identifies flyoutPage.Toolbar == _toolbar as the sole sufficient invariant because the toolbar is only ever assigned to a FlyoutPage when flyoutPage.Parent is IWindow (the setup path at line 606, which is untouched by this PR).

Agreement: Full agreement. The asymmetry between setup (keeps flyoutPage.Parent is IWindow) and teardown (drops it) is intentional and correct — setup must be conservative, teardown must be thorough.


Findings

⚠️ Warning — CI failure on RunOniOS_MauiNativeAOT ARM64 needs to be confirmed unrelated

The overall maui-pr check is red. The failing sub-job is Run Integration Tests RunOniOS_MauiNativeAOT ARM64 (~8 min — fast failure, well under its normal ~45-minute timeout). This is a NativeAOT iOS integration test that tests template creation/compilation — it has no direct relationship to FlyoutPage toolbar teardown. This failure pattern is consistent with an infrastructure flake, but the log should be verified before merging. All other checks — Windows, Android, macOS builds, Helix unit tests, all other integration tests — are green.

💡 Suggestion — VerifyScreenshot() without retryTimeout after navigation

In Issue33615.cs (test), the final VerifyScreenshot() is called immediately after App.WaitForElement("DetailPage2Label"). The toolbar title update after a PushAsync navigation may lag slightly behind the element appearing on slow CI devices or emulators. Consider:

VerifyScreenshot(retryTimeout: TimeSpan.FromSeconds(2));

This matches the pattern in the UI test guidelines and the built-in retry behavior for animations.

💡 Suggestion — PlatformAffected.Android on [Issue] while test runs everywhere

Issue33615.cs (HostApp) declares PlatformAffected.Android, but the test has no platform skip and snapshots are provided for Android, iOS, iOS-26, macOS, and Windows. The PlatformAffected attribute is informational metadata (it doesn't gate test execution), so this isn't functionally wrong. But it may mislead readers into thinking the scenario is Android-only, when the toolbar freeze can plausibly occur on any platform. Consider PlatformAffected.All.


Devil's Advocate

Is removing flyoutPage.Parent is IWindow safe across all scenarios?

The critical question is: can flyoutPage.Toolbar == _toolbar be true when we should not clear the toolbar?

Looking at all paths where flyoutPage.Toolbar = _toolbar is set (lines 608-609 — the only such assignment in OnWindowChanged), the assignment only happens inside the guard if (flyoutPage != null && flyoutPage.Parent is IWindow). So flyoutPage.Toolbar == _toolbar being true implies that at the time of setup, the FlyoutPage was a direct window child. After the window swap, Parent may have changed, but that doesn't mean we should preserve the stale toolbar reference. The teardown code is exactly the right place to clean it up regardless of the current parent state.

Could this affect the non-swap teardown path (normal app close)?

In the normal path where the app closes and the FlyoutPage is still Window.Page, flyoutPage.Parent is IWindow would be true — so both old and new code would clear the toolbar. No regression there.

Is the fix cross-platform, even though the issue is labeled Android?

Yes — NavigationPage.cs is shared code. The root cause is a timing/ordering issue in the element hierarchy, which can technically occur on any platform where Window.Page is swapped. The test snapshots confirm it was tested on iOS and macOS too.


Verdict: NEEDS_DISCUSSION

Confidence: high

Summary: The one-line fix is logically sound and well-justified. The flyoutPage.Toolbar == _toolbar invariant is sufficient on its own. The test is complete and follows MAUI conventions. The only blocker is the RunOniOS_MauiNativeAOT ARM64 CI failure — it looks like an infra flake (8-minute fast-fail, unrelated subsystem), but should be confirmed before merging. If that failure is pre-existing or unrelated, this is ready to LGTM.

Blast Radius: Narrow. The change only affects the teardown path of OnWindowChanged when a NavigationPage is nested inside a FlyoutPage and the window is being cleared. It does not affect the setup path, modal toolbar, or non-flyout cases.


🔧 Fix — Analysis & Comparison

Fix Candidates

# Source Approach Test Result Files Changed Notes
1 try-fix (claude-opus-4.6) Cache FlyoutPage _toolbarFlyoutPage field on NavigationPage; use at teardown instead of tree-walking ✅ PASS NavigationPage.cs (+4/-3) Sidesteps broken parent hierarchy entirely
2 try-fix (claude-sonnet-4.6) In FindMyToolbar(), detect disconnected NavigationPageToolbar via ToolbarTracker.Target == null; discard stale toolbar ✅ PASS NavigationPage.cs (+12/-0) Self-healing approach at detection rather than teardown
3 try-fix (gpt-5.3-codex) Fallback re-lookup from this.FindParentOfType<FlyoutPage>() when flyoutPage.Parent is not IWindow ✅ PASS NavigationPage.cs (+4/-1) Hybrid approach using both toolbar-tree and nav-page tree
4 try-fix (gemini-3-pro-preview) ⛔ BLOCKED Model unavailable
5 try-fix (claude-sonnet-4.6, cross-poll) Use (_toolbar as NavigationPageToolbar)?.RootPage as FlyoutPage at teardown — leverages existing strong reference ✅ PASS NavigationPage.cs (+2/-2) Most elegant: no new fields, uses existing toolbar property
PR PR #34839 Remove flyoutPage.Parent is IWindow && from teardown guard ✅ PASSED (Gate) NavigationPage.cs (+1/-1) Original PR — minimal 1-line fix

Cross-Pollination

Model Round New Ideas? Details
claude-opus-4.6 1 Yes FlyoutPage.ParentChanged subscription for proactive cleanup
claude-sonnet-4.6 1 Yes Use NavigationPageToolbar.RootPage at teardown (→ Attempt 5)
gpt-5.3-codex 1 Yes Track IToolbarElement _toolbarOwner (variant of Attempt 1)
claude-opus-4.6 2 Yes FlyoutPage.OnParentSet() self-healing on re-attach
gpt-5.3-codex 2 Yes Move cleanup into NavigationPageToolbar.Disconnect() itself

Exhausted: Yes (max rounds reached; remaining ideas are variants of explored approaches)

Selected Fix: PR's fix — Remove flyoutPage.Parent is IWindow && (1-line change)

Reason: The PR's fix is the most minimal and directly correct: it removes a condition that was logically wrong (the ordering guarantee it depended on doesn't hold). All 5 alternative approaches also work but are more complex. Attempt 5 (RootPage cast, +2/-2) is the most elegant alternative and worth noting to the author as a slightly more robust variant. The PR's fix fixes the root cause directly without introducing new type casts, fields, or subscriptions.


📋 Report — Final Recommendation

✅ Final Recommendation: APPROVE

Phase Status

Phase Status Notes
Pre-Flight ✅ COMPLETE Issue #33615 analyzed, 8 files classified (1 fix, 2 test, 5 snapshots)
Code Review NEEDS_DISCUSSION (high) 0 errors, 1 warning (CI flake), 2 suggestions
Gate ✅ PASSED Android — tests FAIL without fix, PASS with fix
Try-Fix ✅ COMPLETE 5 attempts (4 PASS, 1 BLOCKED/model unavailable), 2 cross-pollination rounds
Report ✅ COMPLETE

Code Review Impact on Try-Fix

The code review found no ❌ Errors — the only ⚠️ Warning was the RunOniOS_MauiNativeAOT ARM64 CI flake (unrelated to the fix). The blast radius assessment (narrow: only the FlyoutPage teardown path) helped focus all try-fix attempts on NavigationPage.cs exclusively. No model proposed changes to unrelated files. The failure-mode probe confirming flyoutPage.Toolbar == _toolbar is sufficient as an invariant was validated by all 5 passing attempts — every approach that kept this check passed; none were led astray by it.

Summary

PR #34839 fixes a frozen toolbar title in FlyoutPage after a Window.Page swap on Android. The single-line fix in NavigationPage.OnWindowChanged removes a guard condition that was logically incorrect under certain page-swap ordering. The fix is minimal, targeted, and well-tested. Five independent alternative approaches were explored during try-fix — all passing — confirming the root cause analysis is correct and the fix approach is sound. The CI failure (RunOniOS_MauiNativeAOT ARM64) is unrelated to the changes in this PR (iOS NativeAOT template test, ~8-minute fast-fail, no FlyoutPage code involved).

Root Cause

When Window.Page is swapped from a FlyoutPage-containing hierarchy to a plain ContentPage, the element parent chain is reorganized synchronously. flyoutPage.Parent is nulled before the Window=null event propagates to NavigationPage. The old guard flyoutPage.Parent is IWindow && flyoutPage.Toolbar == _toolbar evaluated false because Parent was already null — leaving the stale, disconnected toolbar attached to FlyoutPage.Toolbar. When FlyoutPage was later restored as Window.Page, FindMyToolbar() found the stale toolbar and returned early, preventing creation of a new connected toolbar. The fix removes the parent-check from teardown; flyoutPage.Toolbar == _toolbar is the correct and sufficient invariant.

Fix Quality

The fix is excellent: 1 implementation line changed, correct invariant preserved, complete UI test added with snapshots for 5 platforms, root cause explanation is accurate and detailed. The PR author resolved both previous inline review comments (Mac snapshot present, test passed CI without needing retryTimeout). An even more elegant alternative exists — using (_toolbar as NavigationPageToolbar)?.RootPage as FlyoutPage (+2/-2 lines) to avoid tree-walking entirely — but this is an optional improvement, not a blocker.


@MauiBot MauiBot added s/agent-approved AI agent recommends approval - PR fix is correct and optimal s/agent-fix-pr-picked AI could not beat the PR fix - PR is the best among all candidates labels Apr 15, 2026
@MauiBot MauiBot added the s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review) label Apr 15, 2026
@kubaflo kubaflo changed the base branch from main to inflight/current April 16, 2026 11:29
@kubaflo kubaflo merged commit c96e7e7 into dotnet:inflight/current Apr 16, 2026
34 of 37 checks passed
devanathan-vaithiyanathan pushed a commit to Tamilarasan-Paranthaman/maui that referenced this pull request Apr 21, 2026
…ng a NonFlyOutPage (dotnet#34839)

<!-- 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!

### Issue Details:

Title of FlyOutPage is not updating anymore after showing a
NonFlyOutPage in Android Platform.
       
### Root Cause:

When Window.Page is swapped from a FlyoutPage to a plain ContentPage,
the Window.Page setter clears flyoutPage.Parent = null synchronously
before propagating the Window=null event through the element hierarchy.
This means by the time NavigationPage.OnWindowChanged(null) fires to
clean up the toolbar, the check flyoutPage.Parent is IWindow evaluates
to false (Parent is already null). As a result, flyoutPage.Toolbar =
null never executes — the stale, now-disconnected NavigationPageToolbar
remains attached to
FlyoutPage.Toolbar. When FlyoutPage is later restored as Window.Page,
FindMyToolbar() traverses the ancestors, finds the stale toolbar on
FlyoutPage.Toolbar, and returns early — no new connected toolbar is
created. Since the stale toolbar's ToolbarTracker subscriptions were
severed by Disconnect(), it never receives CurrentPage change
notifications, so the title is permanently frozen.

### Description of Change:

Remove the flyoutPage.Parent is IWindow && condition from the guard in
NavigationPage.OnWindowChanged(null) in NavigationPage.cs. The remaining
check flyoutPage.Toolbar == _toolbar is the correct and sufficient
invariant — it ensures we only clear the toolbar that this
NavigationPage created. The Parent is IWindow guard was redundant in the
normal case (when FlyoutPage is the root page, Parent is IWindow), but
fatally incorrect during a Window.Page swap because the ordering
guarantee it depended on didn't hold.

**Tested the behavior in the following platforms:**

- [x] Android
- [ ] Windows
- [x] iOS
- [x] Mac

### Reference:

N/A

### Issues Fixed:

Fixes  dotnet#33615         

### Screenshots
| Before  | After  |
|---------|--------|
| <Video
src="https://github.com/user-attachments/assets/00869428-8cb4-43a8-981b-7ac6b018e184"
Width="300" Height="600"> | <Video
src="https://github.com/user-attachments/assets/81f94d6d-8c25-4d08-a699-4b3db32e76c8"
Width="300" Height="600"> |
PureWeen pushed a commit that referenced this pull request Apr 22, 2026
…ng a NonFlyOutPage (#34839)

<!-- 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!

### Issue Details:

Title of FlyOutPage is not updating anymore after showing a
NonFlyOutPage in Android Platform.
       
### Root Cause:

When Window.Page is swapped from a FlyoutPage to a plain ContentPage,
the Window.Page setter clears flyoutPage.Parent = null synchronously
before propagating the Window=null event through the element hierarchy.
This means by the time NavigationPage.OnWindowChanged(null) fires to
clean up the toolbar, the check flyoutPage.Parent is IWindow evaluates
to false (Parent is already null). As a result, flyoutPage.Toolbar =
null never executes — the stale, now-disconnected NavigationPageToolbar
remains attached to
FlyoutPage.Toolbar. When FlyoutPage is later restored as Window.Page,
FindMyToolbar() traverses the ancestors, finds the stale toolbar on
FlyoutPage.Toolbar, and returns early — no new connected toolbar is
created. Since the stale toolbar's ToolbarTracker subscriptions were
severed by Disconnect(), it never receives CurrentPage change
notifications, so the title is permanently frozen.

### Description of Change:

Remove the flyoutPage.Parent is IWindow && condition from the guard in
NavigationPage.OnWindowChanged(null) in NavigationPage.cs. The remaining
check flyoutPage.Toolbar == _toolbar is the correct and sufficient
invariant — it ensures we only clear the toolbar that this
NavigationPage created. The Parent is IWindow guard was redundant in the
normal case (when FlyoutPage is the root page, Parent is IWindow), but
fatally incorrect during a Window.Page swap because the ordering
guarantee it depended on didn't hold.

**Tested the behavior in the following platforms:**

- [x] Android
- [ ] Windows
- [x] iOS
- [x] Mac

### Reference:

N/A

### Issues Fixed:

Fixes  #33615         

### Screenshots
| Before  | After  |
|---------|--------|
| <Video
src="https://github.com/user-attachments/assets/00869428-8cb4-43a8-981b-7ac6b018e184"
Width="300" Height="600"> | <Video
src="https://github.com/user-attachments/assets/81f94d6d-8c25-4d08-a699-4b3db32e76c8"
Width="300" Height="600"> |
PureWeen pushed a commit that referenced this pull request Apr 28, 2026
…ng a NonFlyOutPage (#34839)

<!-- 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!

### Issue Details:

Title of FlyOutPage is not updating anymore after showing a
NonFlyOutPage in Android Platform.
       
### Root Cause:

When Window.Page is swapped from a FlyoutPage to a plain ContentPage,
the Window.Page setter clears flyoutPage.Parent = null synchronously
before propagating the Window=null event through the element hierarchy.
This means by the time NavigationPage.OnWindowChanged(null) fires to
clean up the toolbar, the check flyoutPage.Parent is IWindow evaluates
to false (Parent is already null). As a result, flyoutPage.Toolbar =
null never executes — the stale, now-disconnected NavigationPageToolbar
remains attached to
FlyoutPage.Toolbar. When FlyoutPage is later restored as Window.Page,
FindMyToolbar() traverses the ancestors, finds the stale toolbar on
FlyoutPage.Toolbar, and returns early — no new connected toolbar is
created. Since the stale toolbar's ToolbarTracker subscriptions were
severed by Disconnect(), it never receives CurrentPage change
notifications, so the title is permanently frozen.

### Description of Change:

Remove the flyoutPage.Parent is IWindow && condition from the guard in
NavigationPage.OnWindowChanged(null) in NavigationPage.cs. The remaining
check flyoutPage.Toolbar == _toolbar is the correct and sufficient
invariant — it ensures we only clear the toolbar that this
NavigationPage created. The Parent is IWindow guard was redundant in the
normal case (when FlyoutPage is the root page, Parent is IWindow), but
fatally incorrect during a Window.Page swap because the ordering
guarantee it depended on didn't hold.

**Tested the behavior in the following platforms:**

- [x] Android
- [ ] Windows
- [x] iOS
- [x] Mac

### Reference:

N/A

### Issues Fixed:

Fixes  #33615         

### Screenshots
| Before  | After  |
|---------|--------|
| <Video
src="https://github.com/user-attachments/assets/00869428-8cb4-43a8-981b-7ac6b018e184"
Width="300" Height="600"> | <Video
src="https://github.com/user-attachments/assets/81f94d6d-8c25-4d08-a699-4b3db32e76c8"
Width="300" Height="600"> |
PureWeen pushed a commit that referenced this pull request Apr 29, 2026
…ng a NonFlyOutPage (#34839)

<!-- 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!

### Issue Details:

Title of FlyOutPage is not updating anymore after showing a
NonFlyOutPage in Android Platform.
       
### Root Cause:

When Window.Page is swapped from a FlyoutPage to a plain ContentPage,
the Window.Page setter clears flyoutPage.Parent = null synchronously
before propagating the Window=null event through the element hierarchy.
This means by the time NavigationPage.OnWindowChanged(null) fires to
clean up the toolbar, the check flyoutPage.Parent is IWindow evaluates
to false (Parent is already null). As a result, flyoutPage.Toolbar =
null never executes — the stale, now-disconnected NavigationPageToolbar
remains attached to
FlyoutPage.Toolbar. When FlyoutPage is later restored as Window.Page,
FindMyToolbar() traverses the ancestors, finds the stale toolbar on
FlyoutPage.Toolbar, and returns early — no new connected toolbar is
created. Since the stale toolbar's ToolbarTracker subscriptions were
severed by Disconnect(), it never receives CurrentPage change
notifications, so the title is permanently frozen.

### Description of Change:

Remove the flyoutPage.Parent is IWindow && condition from the guard in
NavigationPage.OnWindowChanged(null) in NavigationPage.cs. The remaining
check flyoutPage.Toolbar == _toolbar is the correct and sufficient
invariant — it ensures we only clear the toolbar that this
NavigationPage created. The Parent is IWindow guard was redundant in the
normal case (when FlyoutPage is the root page, Parent is IWindow), but
fatally incorrect during a Window.Page swap because the ordering
guarantee it depended on didn't hold.

**Tested the behavior in the following platforms:**

- [x] Android
- [ ] Windows
- [x] iOS
- [x] Mac

### Reference:

N/A

### Issues Fixed:

Fixes  #33615         

### Screenshots
| Before  | After  |
|---------|--------|
| <Video
src="https://github.com/user-attachments/assets/00869428-8cb4-43a8-981b-7ac6b018e184"
Width="300" Height="600"> | <Video
src="https://github.com/user-attachments/assets/81f94d6d-8c25-4d08-a699-4b3db32e76c8"
Width="300" Height="600"> |
github-actions Bot pushed a commit that referenced this pull request May 6, 2026
…ng a NonFlyOutPage (#34839)

<!-- 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!

### Issue Details:

Title of FlyOutPage is not updating anymore after showing a
NonFlyOutPage in Android Platform.
       
### Root Cause:

When Window.Page is swapped from a FlyoutPage to a plain ContentPage,
the Window.Page setter clears flyoutPage.Parent = null synchronously
before propagating the Window=null event through the element hierarchy.
This means by the time NavigationPage.OnWindowChanged(null) fires to
clean up the toolbar, the check flyoutPage.Parent is IWindow evaluates
to false (Parent is already null). As a result, flyoutPage.Toolbar =
null never executes — the stale, now-disconnected NavigationPageToolbar
remains attached to
FlyoutPage.Toolbar. When FlyoutPage is later restored as Window.Page,
FindMyToolbar() traverses the ancestors, finds the stale toolbar on
FlyoutPage.Toolbar, and returns early — no new connected toolbar is
created. Since the stale toolbar's ToolbarTracker subscriptions were
severed by Disconnect(), it never receives CurrentPage change
notifications, so the title is permanently frozen.

### Description of Change:

Remove the flyoutPage.Parent is IWindow && condition from the guard in
NavigationPage.OnWindowChanged(null) in NavigationPage.cs. The remaining
check flyoutPage.Toolbar == _toolbar is the correct and sufficient
invariant — it ensures we only clear the toolbar that this
NavigationPage created. The Parent is IWindow guard was redundant in the
normal case (when FlyoutPage is the root page, Parent is IWindow), but
fatally incorrect during a Window.Page swap because the ordering
guarantee it depended on didn't hold.

**Tested the behavior in the following platforms:**

- [x] Android
- [ ] Windows
- [x] iOS
- [x] Mac

### Reference:

N/A

### Issues Fixed:

Fixes  #33615         

### Screenshots
| Before  | After  |
|---------|--------|
| <Video
src="https://github.com/user-attachments/assets/00869428-8cb4-43a8-981b-7ac6b018e184"
Width="300" Height="600"> | <Video
src="https://github.com/user-attachments/assets/81f94d6d-8c25-4d08-a699-4b3db32e76c8"
Width="300" Height="600"> |
@github-actions github-actions Bot locked and limited conversation to collaborators May 17, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-controls-flyoutpage FlyoutPage community ✨ Community Contribution partner/syncfusion Issues / PR's with Syncfusion collaboration platform/android s/agent-approved AI agent recommends approval - PR fix is correct and optimal 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)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Android] Title of FlyOutPage is not updating anymore after showing a NonFlyOutPage

6 participants