#8601·capacitor

[Bug]: SystemBars shrinks the WebView twice for the keyboard on Android 10 (API 29) when WebView >= 140 and viewport-fit=cover

Author: ajrobin19Created Sep 14, 2026Updated Sep 14, 2026
Labelstriage

Capacitor Version

@capacitor/android 8.5.1 (the affected code is unchanged in 8.5.2 and on `main`:
android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java, `initWindowInsetsListener`)
@capacitor/core 8.5.1
@capacitor/keyboard 8.0.5

Other API Details

Node 22.23.2, npm 10
Android: compileSdk/targetSdk 36, minSdk 26, AGP 8.13.0
Affected device: Zebra TC21, Android 10 (API 29), Android System WebView >= 140 (Play-updated)
Not affected: DT50 Android 9 (WebView provider is Chrome 138, the last for Android 9), DT50 Android 13 (WebView 151), Pixel Android 16/17
Page uses <meta name="viewport" content="viewport-fit=cover, ...">
capacitor.config.json: "SystemBars": { "style": "DARK" } (insetsHandling default "css"), no Keyboard config

Platforms Affected

  • iOS
  • Android
  • Web

Current Behavior

On Android 10 (API 29) with a current WebView (>= 140) and a page that declares viewport-fit=cover, opening the soft keyboard on any text field shrinks the WebView by the keyboard height twice. On a phone-sized screen the entire WebView disappears and only the window background is visible for as long as the keyboard is up (see the customer video description below: the login page turns into a solid colour when the username field is tapped, and comes back when the keyboard closes). Typing still goes into the invisible field.

Root cause, as far as I can tell from SystemBars.initWindowInsetsListener:

java
boolean shouldPassthroughInsets = getWebViewMajorVersion() >= WEBVIEW_VERSION_WITH_SAFE_AREA_FIX && hasViewportCover;
...
if (shouldPassthroughInsets) {
    // We need to correct for a possible shown IME
    v.setPadding(0, 0, 0, keyboardVisible ? imeInsets.bottom : 0);
  • On API < 30 the activity uses adjustResize, so the framework has already resized the window for the IME by the time this listener runs.
  • On API < 30 WindowInsetsCompat.Type.ime() is the AndroidX compat approximation (system window inset minus stable inset), which still reports the full keyboard height even though the window no longer overlaps the IME.
  • The passthrough branch then pads the WebView's parent by that height again. Net effect: WebView height = screen - 2 x keyboard.

On API 30+ ime() is the real overlap, which is 0 once the window has been resized, so the same line is harmless there. On WebView < 140 the passthrough branch is never taken, so the bug only appears once a device's WebView updates past 140, i.e. it is showing up in the field now on Android 10 devices with Play services, while Android 9 devices (capped at Chrome/WebView 138) never hit it.

Expected Behavior

The WebView keeps the framework-resized height when the keyboard opens on Android 10, exactly as it does on Android 9 (non-passthrough branch) and Android 11+.

Fix that works for us (shipped as a patch-package patch):

diff
             if (shouldPassthroughInsets) {
-                // We need to correct for a possible shown IME
-                v.setPadding(0, 0, 0, keyboardVisible ? imeInsets.bottom : 0);
+                // On API < 30 the window is already resized for the IME (adjustResize) and the
+                // compat ime() inset still reports the keyboard height, so padding again shrinks
+                // the WebView twice. Only pad on API 30+, where ime() is the real overlap.
+                boolean padForIme = keyboardVisible && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R;
+                v.setPadding(0, 0, 0, padForIme ? imeInsets.bottom : 0);

Happy to open a PR with this if you agree with the approach.

Project Reproduction

No public repo yet (I can put one together if needed). Steps, ~10 minutes with an emulator:

  1. npm init @capacitor/app@latest, add Android, keep the default viewport-fit=cover meta. Add a text <input> to the page; make <body> a colour different from the window background so the effect is obvious.
  2. Emulator: system-images;android-29;default;arm64-v8a (Android 10, ships WebView 74). Because that WebView is < 140, temporarily set WEBVIEW_VERSION_WITH_SAFE_AREA_FIX = 0 in node_modules/@capacitor/android/.../SystemBars.java to take the passthrough branch (that is what a real Android 10 device with WebView >= 140 does).
  3. Run, tap the input. The WebView loses twice the keyboard height: on a 1080x2280 emulator ~26% of the page is left above a blank band the size of the keyboard; on a 720x1280 device (Zebra TC21) nothing is left.
  4. Apply the diff above: the WebView keeps the framework-resized height and the input stays visible.
  5. Same build on an Android 16 emulator and on Android 9/13 devices: unchanged.

Additional Information

  • Also transpiling for old WebViews and the WEBVIEW_VERSION_WITH_SAFE_AREA_KEYBOARD_FIX path are unrelated; the issue is purely the API < 30 double shrink.
  • Field impact: rugged Android 10 handhelds (Zebra TC21/TC26 etc.) are common in logistics; as their WebView updates past 140, every Capacitor 8.5 app with viewport-fit=cover will start losing its page whenever the keyboard opens.