[Android] Fix SearchBar text bleeding between instances after navigation#34703
[Android] Fix SearchBar text bleeding between instances after navigation#34703kubaflo merged 3 commits intodotnet:inflight/currentfrom
Conversation
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 34703Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 34703" |
|
Hey there @@SyedAbdulAzeemSF4852! Thank you so much for your PR! Someone from the team will get assigned to your PR shortly and we'll get it reviewed. |
🚦 Gate — Test Verification📊 Expand Full Gate —
|
| # | Type | Test Name | Filter |
|---|---|---|---|
| 1 | UITest | Issue20348 | Issue20348 |
Verification
| Step | Expected | Actual | Result |
|---|---|---|---|
| Without fix | FAIL | FAIL | ✅ |
| With fix | PASS | PASS | ✅ |
Details
- ❌ Failed: SearchBarTextShouldNotBleedToOtherInstances [7 s]
- 📋 Error: First SearchBar should be empty after back navigation — Android state save/restore should not bleed text from other SearchBar instances
Assert.That(result, Is.EqualTo("Pass"))
String lengths are bot...
Fix Files Reverted
eng/pipelines/ci-copilot.ymlsrc/Core/src/Platform/Android/MauiSearchView.cs
Base Branch: main | Merge Base: 720a9d4
🤖 AI Summary📊 Expand Full Review —
|
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| PR | PR #34703 | _queryEditor.SaveEnabled = false in Initialize() |
✅ PASSED (Gate) | MauiSearchView.cs |
Opts internal EditText out of Android state save/restore |
🔧 Fix — Analysis & Comparison
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| 1 | try-fix (claude-opus-4.6) | Override DispatchSaveInstanceState/DispatchRestoreInstanceState on MauiSearchView to use DispatchFreezeSelfOnly/DispatchThawSelfOnly |
✅ PASS | MauiSearchView.cs, PublicAPI.Unshipped.txt |
Blocks all child state propagation at container level |
| 2 | try-fix (claude-sonnet-4.6) | Override DispatchRestoreInstanceState, call base then clear _queryEditor.Text afterward |
✅ PASS | MauiSearchView.cs, PublicAPI.Unshipped.txt |
Allows restore but clears bled text; note: clears all editor text post-restore |
| 3 | try-fix (gpt-5.3-codex) | SaveEnabled = false + SaveFromParentEnabled = false on MauiSearchView container |
✅ PASS | MauiSearchView.cs |
Disables state save on parent container; needed both flags |
| 4 | try-fix (gpt-5.4, gemini unavailable) | Override OnSaveInstanceState() to return null on MauiSearchView |
❌ FAIL | MauiSearchView.cs |
Child EditText state still bleeds; overriding only parent save state is insufficient |
| PR | PR #34703 | _queryEditor.SaveEnabled = false in Initialize() |
✅ PASSED (Gate) | MauiSearchView.cs |
Targets only child EditText |
Cross-Pollination
| Model | Round | New Ideas? | Details |
|---|---|---|---|
| claude-opus-4.6 | 2 | "New" (already tried) | View.GenerateViewId() on _queryEditor — already known to crash MAUI internals |
| claude-sonnet-4.6 | 2 | Novel | Custom EditText subclass overriding save/restore — overly complex, not worth additional attempt |
| gpt-5.3-codex | 2 | "New" (already tried) | Unique view ID — already tried/failed |
| gpt-5.4 | 2 | "New" (already tried) | View.NoId — same root issue as unique ID approach |
Exhausted: Yes — no actionable new ideas beyond those already tested
Selected Fix: PR's fix (_queryEditor.SaveEnabled = false) — Reason: simplest (1 file, 10 lines), most targeted (only affects the specific child), no public API changes, no virtual method overrides, directly addresses root cause
📋 Report — Final Recommendation
✅ Final Recommendation: APPROVE
Phase Status
| Phase | Status | Notes |
|---|---|---|
| Pre-Flight | ✅ COMPLETE | Issue #20348, Android-only, 1 fix file + 2 test files |
| Gate | ✅ PASSED | Android — test fails without fix, passes with fix |
| Try-Fix | ✅ COMPLETE | 4 attempts (3 pass, 1 fail); PR's fix selected as best |
| Report | ✅ COMPLETE |
Summary
PR #34703 fixes a long-standing Android bug where text from one SearchBar is incorrectly applied to other SearchBar instances after back navigation. The fix is minimal (10 lines in one file), well-targeted, and verified both by the Gate and by 3 independent try-fix attempts that each found working alternatives — all of which are more complex than the PR's approach. The PR's fix is the best solution.
Root Cause
Android's instance state system uses view resource IDs as keys. Every SearchView contains an internal EditText with the fixed resource ID search_src_text. When multiple SearchBar controls exist on the same page, they all share this state slot. On save, the last written value wins; on restore (back navigation), that value is applied to all instances — causing text bleeding.
Fix Quality
The PR's fix (_queryEditor.SaveEnabled = false) is the optimal approach:
- Minimal: 10 lines in one file, no public API changes, no virtual method overrides
- Targeted: Disables save only on the specific child EditText with the colliding ID, leaving all other view state unaffected
- Correct: Directly addresses the root cause by opting out of the state collision entirely
- Well-documented: Includes a clear comment explaining the Android ID collision issue and why this fix resolves it
- Verified: Gate passed; 3 independent alternative approaches also passed but all require more code (public API overrides, multiple flags, or more complex logic)
Comparison with alternatives found in Try-Fix:
| Approach | Lines | API Changes | Risk |
|---|---|---|---|
PR: _queryEditor.SaveEnabled = false |
~10 | None | ✅ Lowest |
Attempt 1: DispatchFreezeSelfOnly/DispatchThawSelfOnly |
~19 | PublicAPI.Unshipped.txt | Medium (public API) |
| Attempt 2: Override restore + clear text | ~21 | PublicAPI.Unshipped.txt | Medium (clears all text always) |
Attempt 3: SaveEnabled+SaveFromParentEnabled on parent |
~5 | None | Low-medium (broader scope) |
Code Quality Notes
- The fix is clean — the comment explains why
SaveEnabled = falseis set, which is important for future maintainers. - The null-check (
if (_queryEditor is not null)) is appropriate sinceGetFirstChildOfType<EditText>()can return null. - Tests are well-structured: HostApp page sets up a clear Pass/Fail indicator label, and the NUnit test uses straightforward element queries without fragile screenshot comparisons.
- The PR is currently marked as draft — this should be converted to ready-for-review before merge.
- Only Android is validated by the author (correct, as this is Android-only bug).
Selected Fix: PR
kubaflo
left a comment
There was a problem hiding this comment.
Looks good! Is it ready?
@kubaflo yes, its ready now. |
There was a problem hiding this comment.
Pull request overview
Fixes an Android-specific SearchBar state-restoration issue where text can “bleed” between multiple SearchBar instances after navigation, and adds a regression UI test for the scenario.
Changes:
- Disable Android instance-state saving for the internal
EditTextwithinMauiSearchViewto prevent cross-instance state restoration. - Add a HostApp repro page for Issue #20348 using multiple SearchBars and back navigation.
- Add an Appium-based UI test validating text does not propagate between SearchBars after navigating back.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| src/Core/src/Platform/Android/MauiSearchView.cs | Disables instance-state save/restore on the internal query editor to prevent state collisions between SearchViews. |
| src/Controls/tests/TestCases.HostApp/Issues/Issue20348.cs | Adds a navigation-based repro page with two SearchBars and a pass/fail indicator. |
| src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue20348.cs | Adds an automated UI test for the repro page to prevent regressions. |
| // Disable Android's built-in instance state saving on the internal EditText | ||
| // to prevent query text from being incorrectly restored across multiple | ||
| // SearchView instances during navigation. The EditText shares a fixed | ||
| // resource ID (search_src_text) across all SearchViews, causing state | ||
| // to bleed between instances. | ||
| if (_queryEditor is not null) | ||
| { | ||
| _queryEditor.SaveEnabled = false; | ||
| } |
There was a problem hiding this comment.
Setting _queryEditor.SaveEnabled = false prevents Android instance-state save/restore for the query editor entirely, which can change user-visible behavior (e.g., typed SearchBar text may no longer be restored after Activity recreation/config changes). Consider preserving per-SearchBar query state via a SearchView-level save/restore implementation (or another per-instance mechanism) while still preventing cross-instance ID collisions.
| firstSearchBar.TextChanged += (s, e) => | ||
| { | ||
| firstSearchBarTextLabel.Text = string.IsNullOrEmpty(e.NewTextValue) | ||
| ? "Pass" | ||
| : "Fail"; | ||
| }; |
There was a problem hiding this comment.
FirstSearchBarText is updated only via firstSearchBar.TextChanged. If the Android state-restore path updates the platform text without raising the managed TextChanged event (or does so before the handler is attached in future refactors), this issue page can show "Pass" while the first SearchBar actually contains leaked text, making the UI test a false negative. Consider also updating the label based on firstSearchBar.Text in OnAppearing (or after returning from navigation).
| App.WaitForElement("FirstSearchBar"); | ||
| App.WaitForElement("SecondSearchBar"); | ||
|
|
||
| App.EnterText("SecondSearchBar", "TestText"); |
There was a problem hiding this comment.
After App.EnterText("SecondSearchBar", ...), the soft keyboard may obscure NavigateButton on smaller devices, making App.Tap("NavigateButton") flaky. Consider dismissing the keyboard (e.g., App.DismissKeyboard()) and/or waiting for NavigateButton to be visible before tapping.
| App.EnterText("SecondSearchBar", "TestText"); | |
| App.EnterText("SecondSearchBar", "TestText"); | |
| App.DismissKeyboard(); | |
| App.WaitForElement("NavigateButton"); |
…ion (#34703) <!-- 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 - On Android, when a page contains multiple SearchBar controls, navigating away (e.g., via NavigationPage.Push) and returning causes the text from one SearchBar to be incorrectly applied to the others. ### Root Cause - Android saves and restores view state using the view’s resource ID as the key. The internal EditText inside every SearchView uses the same fixed ID (search_src_text). - When multiple SearchBar instances are present on the same page, their state gets overwritten during the save process, leaving only the last SearchBar’s text preserved. During restore (e.g., after back navigation), this saved text is applied to all SearchBar instances sharing that ID, causing them to incorrectly display the same value. ### Description of Change - Set SaveEnabled = false on the internal EditText of MauiSearchView to opt out of Android's instance state save/restore cycle, preventing stale text from being incorrectly applied across SearchBar instances during navigation. ### Issues Fixed Fixes #20348 ### Validated the behaviour in the following platforms - [ ] Windows - [x] Android - [ ] iOS - [ ] Mac ### Output | Before | After | |----------|----------| | <video src="https://github.com/user-attachments/assets/48d70677-fa4f-4287-a55f-202e69da64c5"> | <video src="https://github.com/user-attachments/assets/cc42aa54-4450-46c6-864b-74266c780ddd"> |
…ion (dotnet#34703) <!-- 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 - On Android, when a page contains multiple SearchBar controls, navigating away (e.g., via NavigationPage.Push) and returning causes the text from one SearchBar to be incorrectly applied to the others. ### Root Cause - Android saves and restores view state using the view’s resource ID as the key. The internal EditText inside every SearchView uses the same fixed ID (search_src_text). - When multiple SearchBar instances are present on the same page, their state gets overwritten during the save process, leaving only the last SearchBar’s text preserved. During restore (e.g., after back navigation), this saved text is applied to all SearchBar instances sharing that ID, causing them to incorrectly display the same value. ### Description of Change - Set SaveEnabled = false on the internal EditText of MauiSearchView to opt out of Android's instance state save/restore cycle, preventing stale text from being incorrectly applied across SearchBar instances during navigation. ### Issues Fixed Fixes dotnet#20348 ### Validated the behaviour in the following platforms - [ ] Windows - [x] Android - [ ] iOS - [ ] Mac ### Output | Before | After | |----------|----------| | <video src="https://github.com/user-attachments/assets/48d70677-fa4f-4287-a55f-202e69da64c5"> | <video src="https://github.com/user-attachments/assets/cc42aa54-4450-46c6-864b-74266c780ddd"> |
…ion (#34703) <!-- 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 - On Android, when a page contains multiple SearchBar controls, navigating away (e.g., via NavigationPage.Push) and returning causes the text from one SearchBar to be incorrectly applied to the others. ### Root Cause - Android saves and restores view state using the view’s resource ID as the key. The internal EditText inside every SearchView uses the same fixed ID (search_src_text). - When multiple SearchBar instances are present on the same page, their state gets overwritten during the save process, leaving only the last SearchBar’s text preserved. During restore (e.g., after back navigation), this saved text is applied to all SearchBar instances sharing that ID, causing them to incorrectly display the same value. ### Description of Change - Set SaveEnabled = false on the internal EditText of MauiSearchView to opt out of Android's instance state save/restore cycle, preventing stale text from being incorrectly applied across SearchBar instances during navigation. ### Issues Fixed Fixes #20348 ### Validated the behaviour in the following platforms - [ ] Windows - [x] Android - [ ] iOS - [ ] Mac ### Output | Before | After | |----------|----------| | <video src="https://github.com/user-attachments/assets/48d70677-fa4f-4287-a55f-202e69da64c5"> | <video src="https://github.com/user-attachments/assets/cc42aa54-4450-46c6-864b-74266c780ddd"> |
…ion (dotnet#34703) <!-- 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 - On Android, when a page contains multiple SearchBar controls, navigating away (e.g., via NavigationPage.Push) and returning causes the text from one SearchBar to be incorrectly applied to the others. ### Root Cause - Android saves and restores view state using the view’s resource ID as the key. The internal EditText inside every SearchView uses the same fixed ID (search_src_text). - When multiple SearchBar instances are present on the same page, their state gets overwritten during the save process, leaving only the last SearchBar’s text preserved. During restore (e.g., after back navigation), this saved text is applied to all SearchBar instances sharing that ID, causing them to incorrectly display the same value. ### Description of Change - Set SaveEnabled = false on the internal EditText of MauiSearchView to opt out of Android's instance state save/restore cycle, preventing stale text from being incorrectly applied across SearchBar instances during navigation. ### Issues Fixed Fixes dotnet#20348 ### Validated the behaviour in the following platforms - [ ] Windows - [x] Android - [ ] iOS - [ ] Mac ### Output | Before | After | |----------|----------| | <video src="https://github.com/user-attachments/assets/48d70677-fa4f-4287-a55f-202e69da64c5"> | <video src="https://github.com/user-attachments/assets/cc42aa54-4450-46c6-864b-74266c780ddd"> |
…ion (#34703) <!-- 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 - On Android, when a page contains multiple SearchBar controls, navigating away (e.g., via NavigationPage.Push) and returning causes the text from one SearchBar to be incorrectly applied to the others. ### Root Cause - Android saves and restores view state using the view’s resource ID as the key. The internal EditText inside every SearchView uses the same fixed ID (search_src_text). - When multiple SearchBar instances are present on the same page, their state gets overwritten during the save process, leaving only the last SearchBar’s text preserved. During restore (e.g., after back navigation), this saved text is applied to all SearchBar instances sharing that ID, causing them to incorrectly display the same value. ### Description of Change - Set SaveEnabled = false on the internal EditText of MauiSearchView to opt out of Android's instance state save/restore cycle, preventing stale text from being incorrectly applied across SearchBar instances during navigation. ### Issues Fixed Fixes #20348 ### Validated the behaviour in the following platforms - [ ] Windows - [x] Android - [ ] iOS - [ ] Mac ### Output | Before | After | |----------|----------| | <video src="https://github.com/user-attachments/assets/48d70677-fa4f-4287-a55f-202e69da64c5"> | <video src="https://github.com/user-attachments/assets/cc42aa54-4450-46c6-864b-74266c780ddd"> |
…ion (#34703) <!-- 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 - On Android, when a page contains multiple SearchBar controls, navigating away (e.g., via NavigationPage.Push) and returning causes the text from one SearchBar to be incorrectly applied to the others. ### Root Cause - Android saves and restores view state using the view’s resource ID as the key. The internal EditText inside every SearchView uses the same fixed ID (search_src_text). - When multiple SearchBar instances are present on the same page, their state gets overwritten during the save process, leaving only the last SearchBar’s text preserved. During restore (e.g., after back navigation), this saved text is applied to all SearchBar instances sharing that ID, causing them to incorrectly display the same value. ### Description of Change - Set SaveEnabled = false on the internal EditText of MauiSearchView to opt out of Android's instance state save/restore cycle, preventing stale text from being incorrectly applied across SearchBar instances during navigation. ### Issues Fixed Fixes #20348 ### Validated the behaviour in the following platforms - [ ] Windows - [x] Android - [ ] iOS - [ ] Mac ### Output | Before | After | |----------|----------| | <video src="https://github.com/user-attachments/assets/48d70677-fa4f-4287-a55f-202e69da64c5"> | <video src="https://github.com/user-attachments/assets/cc42aa54-4450-46c6-864b-74266c780ddd"> |
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
Root Cause
Description of Change
Issues Fixed
Fixes #20348
Validated the behaviour in the following platforms
Output
Before.mov
After.mov