Skip to content

feat(SystemBars): add keyboardInsetsHandling option to let the keyboard overlay the webview - #8612

Closed
piotrblasiak wants to merge 1 commit into
ionic-team:mainfrom
piotrblasiak:systembars-keyboard-insets-none
Closed

piotrblasiak wants to merge 1 commit into
ionic-team:mainfrom
piotrblasiak:systembars-keyboard-insets-none

Conversation

@piotrblasiak

Copy link
Copy Markdown

Description

Adds a SystemBars.keyboardInsetsHandling config option for Android:

  • resize (default): current behavior, the webview is padded by the IME inset while the keyboard is visible.
  • none: the webview is left untouched and the keyboard overlays it.

With none, the insets listener treats the keyboard as not visible (so no IME padding is applied) and sets the IME insets it returns to Insets.NONE / not visible. The second part is needed because WebView >= 139 resizes its visual viewport for the IME insets it receives, which makes the whole page pannable above the keyboard. Zeroing rather than consuming follows the Android guidance and matches what this listener already does for system bar insets.

Change Type

  • Fix
  • Feature
  • Refactor
  • Breaking Change
  • Documentation

Rationale / Problems Fixed

closes #8611

Since #8384 (8.3.0) the webview is always resized for the keyboard, even when the activity declares android:windowSoftInputMode="adjustNothing". Apps that keep content clear of the keyboard themselves, using the height from @capacitor/keyboard as they do on iOS with resize: "none", now account for the keyboard twice, and there is no way to opt out other than insetsHandling: "disable", which also drops the safe area handling. This was asked for in #8384 (comment) and is what ionic-team/capacitor-keyboard#63 tries to solve from the keyboard plugin's side.

Nothing changes unless the option is set.

Tests or Reproductions

Reproduction: https://github.com/piotrblasiak/capacitor-keyboard-overlay-repro (fresh npm init @capacitor/app + adjustNothing + an input and a fixed bottom bar; shows window.innerHeight and visualViewport.height).

  • Without the option: window.innerHeight shrinks by the keyboard height and the fixed bar jumps above the keyboard.
  • With keyboardInsetsHandling: "none": both heights stay unchanged, the bar stays under the keyboard, the page cannot be panned, and keyboardWillShow / keyboardWillHide still fire with the correct height (the keyboard plugin reads the root window insets).

Tested with the reproduction project on a Zebra TC58 (Android 15, Android System WebView 151.0.7922.199).

Platforms Affected

  • Android
  • iOS
  • Web

Notes / Comments

Happy to rename the option or key it off windowSoftInputMode="adjustNothing" instead if you prefer that over a new config value.

@tafelnl

tafelnl commented Sep 20, 2026 •

Copy link
Copy Markdown
Contributor

I don't think it's wise to introduce yet another config variable. Better would be to deprecate/remove the keyboard resizing stuff altogether (in the @capacitor/keyboard plugin) and keep it as closely as native as possible instead (and maintaining a SSOT). That could mean that we detect the value provided in the androidmanifest and act upon that.

That being said, may I ask for what use case you would want to set windowSoftInputMode to adjustNothing?

@piotrblasiak

Copy link
Copy Markdown
Author

windowSoftInputMode adjustNothing is needed if you want to control what the keyboard covers in the app. In my case, i want it to cover some controls that are not needed when in keyboard input mode, leaving more room for other things.

@tafelnl what do you suggest?

@tafelnl

tafelnl commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

@tafelnl what do you suggest?

Detecting the value in the manifest

That could mean that we detect the value provided in the androidmanifest and act upon that.

In my case, i want it to cover some controls that are not needed when in keyboard input mode

So why not hide those instead? I'm trying to understand the use case, as that helps solving the issue

@piotrblasiak

Copy link
Copy Markdown
Author

So why not hide those instead? I'm trying to understand the use case, as that helps solving the issue

Because the controls (in my case a tab bar) follow the bottom border of the window, so hiding them would either mean you would have to just make them disappear/reappear instantly when the keyboard starts/stops animating or it would look very awkward. See attached video of how it looks when it works correctly.

ScreenRecording_09-21-2026.07-40-49_1.MP4

@piotrblasiak

Copy link
Copy Markdown
Author

@tafelnl but most of all I think the behavior this introduced should be considered a rather critical bug. In my case, this made the app unexpectedly completely useless - as my table calculates the remaining window height that it can use and it already took into account the keyboard height - so all my tables broke. If someone sets windowSoftInputMode to adjustNothing, itäs probably for a good reason and should be respected?

@tafelnl

tafelnl commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

If someone sets windowSoftInputMode to adjustNothing, itäs probably for a good reason and should be respected?

Probably yes. But it's always good to understand the whole picture so that we end up implementing a correct fix.

Some background information that some might find useful:

The changes around the safe area have introduced several other bugs in the past (some of which might have occurred in your app without your knowledge). It's unfortunate that we didn't get a lot of time from Google to implement a proper fix, or moreover that Google doesn't take care of this in their own webview itself like Apple does. Google was also delaying answers/fixes on bugs raises in their issuetracker for some reason. Only when the Capacitor community (and probably other developers using a webview) pushed again and again they made some progress finally. On the other hand the Capacitor team should've probably take more care when tackling this issue and they should have released a beta/preview version first. That - btw - seems to be an issue within the Capacitor community in general. There's not a lot of testing going on. It kind of makes sense though, because developers using Capacitor are generally not experienced native developers, so it's hard to test and debug native code anyways.

Anyways, a proper fix should indeed be implemented. Without having dug into this I would suggest to do what I mentioned before: remove the logic from keyboard plugin entirely, as it just causes confusion. Additionally the safe area plugin should detect the value of the windowSoftInputMode and act accordingly

@tafelnl

tafelnl commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

When looking at the Android docs, I see the following:

The activity's main window isn't resized or panned to make room for the soft keyboard. The activity is responsible for making room for the soft keyboard using the window insets. For activities that handle window insets correctly this gives the most control over how the window's contents are displayed on the screen.

Especially this part is interesting:

For activities that handle window insets correctly this gives the most control over how the window's contents are displayed on the screen.

This insinuates that when going for adjustResize (and possibly adjustPan) the activity would resize correctly out of the box. But I don't think that's what actually happens when using a (Capacitor) webview. Because in my testing the activity itself stays full screen and the webview get's all of the safe area insets, including the IME ones. The latter would actually suggest the opposite of the Android docs if you ask me; because why would you receive the IME insets if you are ought to ignore them? Doesn't make sense to me. Moreover it would cause visual glitches and bugs.

As far as I'm concerned, we should detect the following values and act accordingly:

adjustResize -> keep the logic as-is
adjustNothing -> do not resize the window, but we should still pass through the safe area insets for the IME, so a developer can "handle window insets correctly"
adjustPan -> not sure how we should handle this one, but I think it's most similar to adjustNothing from a safe area point of view. However, we cannot guarantee that the webview is going to pan the focuses input into view. Haven't tested it either. It could work (because currently, under normal circumstances, panning happens out of the box with adjustResize). This one needs some more digging

@piotrblasiak piotrblasiak closed this by deleting the head repository Sep 21, 2026
@piotrblasiak

Copy link
Copy Markdown
Author

That - btw - seems to be an issue within the Capacitor community in general. There's not a lot of testing going on.

Yes. I guess a problem is that js developers use playwright by default, which doesn't really support testing on a device or sim.

adjustNothing -> do not resize the window, but we should still pass through the safe area insets for the IME, so a developer can "handle window insets correctly"

You're right - this is the better approach. The one catch is that WebView >= 139 resizes the visual viewport for the IME insets, so the page becomes pannable above the keyboard, but that should be solvable the standard way with navigator.virtualKeyboard.overlaysContent = true (at least on Android, I still need to
verify it on a device). So the only actual bug fix is to skip the padding of the view when the manifest has adjustNothing. I will update the PR to do that, drop the config option, and mention overlaysContent in the docs.

About removing the resize logic from the keyboard plugin, I guess you mean the Android part (resizeOnFullScreen)? On iOS the resize option is the only way to control this, there is no manifest equivalent, so that would need to stay. And the keyboard events with the height are still needed on both platforms, on iOS
there is no VirtualKeyboard API at all.

@piotrblasiak

Copy link
Copy Markdown
Author

@tafelnl I tested passing the IME insets through on device now, and unfortunately it doesn't work: the page still pans above the keyboard even with navigator.virtualKeyboard.overlaysContent = true. It works in Chrome on the same device, so I looked at the Chromium source. Chrome handles overlaysContent /
interactive-widget in its own embedder code, and the WebView path added in M139 applies the visual viewport inset unconditionally, it never checks the keyboard mode. So there is no web-side opt-out in a WebView, only the native one from the Android guide (not handing the WebView the IME insets).

I also managed to delete my fork, so this PR can't be updated anymore. I opened #8619 instead, reworked the way you suggested: no config option, adjustNothing is detected from the window's soft input mode, and in that case the padding is skipped and the IME insets are zeroed. Everything else is unchanged, and the
keyboard height is still available through the keyboard plugin. If WebView gets fixed at some point the zeroing can be dropped again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: SystemBars resizes the WebView for the keyboard even with windowSoftInputMode="adjustNothing" (regression in 8.3.0)

2 participants