diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/SearchBarLandscapeShowsInlineKeyboardNotExtractMode.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/SearchBarLandscapeShowsInlineKeyboardNotExtractMode.png
new file mode 100644
index 000000000000..46f2c304a162
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/SearchBarLandscapeShowsInlineKeyboardNotExtractMode.png differ
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue14708.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue14708.cs
new file mode 100644
index 000000000000..0a81586e0665
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue14708.cs
@@ -0,0 +1,69 @@
+using Microsoft.Maui.Controls;
+
+namespace Maui.Controls.Sample.Issues;
+
+[Issue(IssueTracker.Github, 14708, "Android SearchBar in landscape shows full-screen IME extract mode", PlatformAffected.Android)]
+public class Issue14708 : ContentPage
+{
+ Label _searchTextLabel;
+ SearchBar _primarySearchBar;
+
+ public Issue14708()
+ {
+ _searchTextLabel = new Label
+ {
+ Text = "Search text: (none)",
+ AutomationId = "SearchTextLabel",
+ FontSize = 13
+ };
+
+ _primarySearchBar = new SearchBar
+ {
+ Text = "Hello, landscape!",
+ Placeholder = "Tap here in landscape — keyboard should be inline",
+ AutomationId = "SearchBarControl"
+ };
+
+ var searchBar2 = new SearchBar
+ {
+ Placeholder = "Second SearchBar",
+ AutomationId = "SearchBar2"
+ };
+
+
+ var searchBar3 = new SearchBar
+ {
+ Text = "Hello, landscape!",
+ Placeholder = "Third SearchBar",
+ AutomationId = "SearchBar3"
+ };
+
+
+ Content = new ScrollView
+ {
+ VerticalOptions = LayoutOptions.Fill,
+ Content = new VerticalStackLayout
+ {
+ Padding = new Thickness(16),
+ Spacing = 12,
+ Children =
+ {
+ new Label
+ {
+ Text = "Rotate to LANDSCAPE, then tap any SearchBar. " +
+ "The keyboard should appear inline at the bottom — " +
+ "NOT as a full-screen black overlay.",
+ HorizontalTextAlignment = TextAlignment.Center
+ },
+ _primarySearchBar,
+ _searchTextLabel,
+ new BoxView { HeightRequest = 1, Color = Colors.LightGray },
+ new Label { Text = "Additional SearchBars:", FontAttributes = FontAttributes.Italic, FontSize = 13 },
+ searchBar2,
+ searchBar3
+ }
+ }
+ };
+ }
+
+}
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue14708.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue14708.cs
new file mode 100644
index 000000000000..7f02d242f54a
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue14708.cs
@@ -0,0 +1,39 @@
+#if ANDROID || IOS // Orientation changes and IME behavior are only relevant on mobile platforms
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues;
+
+public class Issue14708 : _IssuesUITest
+{
+ public Issue14708(TestDevice device) : base(device) { }
+
+ public override string Issue => "Android SearchBar in landscape shows full-screen IME extract mode";
+
+ [TearDown]
+ public void TearDown()
+ {
+ App.SetOrientationPortrait();
+ }
+
+ [Test]
+ [Category(UITestCategories.SearchBar)]
+ public void SearchBarLandscapeShowsInlineKeyboardNotExtractMode()
+ {
+ // Rotate to landscape — this is the trigger condition for the bug
+ App.SetOrientationLandscape();
+
+ App.WaitForElement("SearchBarControl");
+
+ // Tap the primary SearchBar to open the keyboard
+ App.Tap("SearchBarControl");
+
+ // In the unfixed state, Android enters IME extract mode in landscape:
+ // a full-screen black overlay replaces the inline keyboard and covers all
+ // page content. VerifyScreenshot() catches this because the visual output
+ // is dramatically different from the fixed (inline-keyboard) baseline.
+ VerifyScreenshot(retryTimeout: TimeSpan.FromSeconds(2));
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/SearchBarLandscapeShowsInlineKeyboardNotExtractMode.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/SearchBarLandscapeShowsInlineKeyboardNotExtractMode.png
new file mode 100644
index 000000000000..1dfe3140b33c
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios-26/SearchBarLandscapeShowsInlineKeyboardNotExtractMode.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SearchBarLandscapeShowsInlineKeyboardNotExtractMode.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SearchBarLandscapeShowsInlineKeyboardNotExtractMode.png
new file mode 100644
index 000000000000..de0dcbf49184
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SearchBarLandscapeShowsInlineKeyboardNotExtractMode.png differ
diff --git a/src/Core/src/Handlers/SearchBar/SearchBarHandler2.Android.cs b/src/Core/src/Handlers/SearchBar/SearchBarHandler2.Android.cs
index ec1ae3644122..cd5d1f31f2ad 100644
--- a/src/Core/src/Handlers/SearchBar/SearchBarHandler2.Android.cs
+++ b/src/Core/src/Handlers/SearchBar/SearchBarHandler2.Android.cs
@@ -252,7 +252,10 @@ void OnEditorAction(object? sender, TextView.EditorActionEventArgs e)
{
var actionId = e.ActionId;
var evt = e.Event;
- ImeAction currentInputImeFlag = PlatformView.EditText.ImeOptions;
+ // Mask out ImeFlags (e.g., NoFullscreen) so we compare only the action bits.
+ // Without the mask, ImeOptions may contain 0x02000000 (NoFullscreen) OR'd with
+ // the action value, causing action comparisons (e.g., ImeAction.Done) to fail.
+ ImeAction currentInputImeFlag = (ImeAction)((int)PlatformView.EditText.ImeOptions & (int)ImeAction.ImeMaskAction);
// On API 34 the issue where actionId is ImeAction.ImeNull when using a hardware keyboard was fixed.
// Normalize it here so the rest of the logic is consistent across API levels.
diff --git a/src/Core/src/Platform/Android/EditTextExtensions.cs b/src/Core/src/Platform/Android/EditTextExtensions.cs
index a81674d035eb..b8f5040b440f 100644
--- a/src/Core/src/Platform/Android/EditTextExtensions.cs
+++ b/src/Core/src/Platform/Android/EditTextExtensions.cs
@@ -510,5 +510,22 @@ static bool RectContainsMotionEvent(global::Android.Graphics.Rect rect, MotionEv
return new global::Android.Graphics.Rect(leftEdge, topEdge, rightEdge, bottomEdge);
}
}
+
+ ///
+ /// Ensures is set on the EditText's ImeOptions,
+ /// preventing the IME from entering full-screen extract mode in landscape orientation.
+ ///
+ ///
+ /// Call this helper after any assignment to editText.ImeOptions inside the
+ /// SearchBar platform code (MauiSearchView and SearchViewExtensions), or the
+ /// NoFullscreen flag will be lost and the landscape IME regression (#14708) will
+ /// silently re-appear. ImeOptions is typed as in the Android
+ /// binding, but it holds combined ImeAction + ImeFlags bits; NoFullscreen is an
+ /// ImeFlags value (0x02000000).
+ ///
+ internal static void EnsureNoFullscreenFlag(this EditText editText)
+ {
+ editText.ImeOptions = (ImeAction)((int)editText.ImeOptions | (int)ImeFlags.NoFullscreen);
+ }
}
-}
\ No newline at end of file
+}
diff --git a/src/Core/src/Platform/Android/MauiSearchView.cs b/src/Core/src/Platform/Android/MauiSearchView.cs
index c2f868d28d6f..385d5f107bba 100644
--- a/src/Core/src/Platform/Android/MauiSearchView.cs
+++ b/src/Core/src/Platform/Android/MauiSearchView.cs
@@ -1,5 +1,6 @@
using Android.Content;
using Android.Views;
+using Android.Views.InputMethods;
using Android.Widget;
using Java.IO;
using SearchView = AndroidX.AppCompat.Widget.SearchView;
@@ -30,6 +31,7 @@ void Initialize()
if (_queryEditor is not null)
{
_queryEditor.SaveEnabled = false;
+ _queryEditor.EnsureNoFullscreenFlag();
}
if (_queryEditor?.LayoutParameters is LinearLayout.LayoutParams layoutParams)
diff --git a/src/Core/src/Platform/Android/SearchViewExtensions.cs b/src/Core/src/Platform/Android/SearchViewExtensions.cs
index 57b42f70a679..dbcc9dce727b 100644
--- a/src/Core/src/Platform/Android/SearchViewExtensions.cs
+++ b/src/Core/src/Platform/Android/SearchViewExtensions.cs
@@ -238,8 +238,22 @@ public static void UpdateKeyboard(this SearchView searchView, ISearchBar searchB
public static void UpdateReturnType(this SearchView searchView, ISearchBar searchBar)
{
- searchView.SetInputType(searchBar);
- searchView.ImeOptions = (int)searchBar.ReturnType.ToPlatform();
+ // Resolve the inner EditText once and reuse it to avoid two tree-walk calls.
+ var editText = searchView.GetFirstChildOfType();
+ searchView.SetInputType(searchBar, editText);
+ // Keep SearchView's own ImeOptions current (including NoFullscreen) so Android
+ // doesn't re-propagate a stale stored value back to the EditText after
+ // configuration changes or focus resets.
+ searchView.ImeOptions = (int)((int)searchBar.ReturnType.ToPlatform() | (int)ImeFlags.NoFullscreen);
+ // Also set directly on the inner EditText: SearchView.setImeOptions propagates to
+ // the inner query EditText on most API levels, but does not reliably forward ImeFlags
+ // (e.g., NoFullscreen) on older APIs. Writing to EditText directly and then calling
+ // EnsureNoFullscreenFlag guarantees the flag is always present regardless of API level.
+ if (editText is not null)
+ {
+ editText.ImeOptions = searchBar.ReturnType.ToPlatform();
+ editText.EnsureNoFullscreenFlag();
+ }
}
internal static void SetInputType(this SearchView searchView, ISearchBar searchBar, EditText? editText = null)
@@ -395,10 +409,12 @@ static bool TryGetDefaultStateColor(TextInputLayout textInputLayout, int attribu
internal static void UpdateReturnType(this EditText editText, ISearchBar searchBar)
{
editText.ImeOptions = searchBar.ReturnType.ToPlatform();
+ editText.EnsureNoFullscreenFlag();
// Restart the input on the current focused EditText
InputMethodManager? imm = (InputMethodManager?)editText.Context?.GetSystemService(Context.InputMethodService);
imm?.RestartInput(editText);
}
+
}
-}
+}
\ No newline at end of file