Skip to content

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

Open

Description

@ajrobin19

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:

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):

             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.

Activity

  1. chbrecht commented on Oct 14, 2026

    @chbrecht

    We hit this in production as well and can confirm the analysis and the fix.

    Field reports (after shipping Capacitor 8): Users on Android 10 devices with a Play-updated WebView could no longer type in our app. Example: Huawei P20 Pro, EMUI 12 / Android 10, WebView 153, SwiftKey. A second user had a Samsung device with Samsung Keyboard. As soon as the keyboard opened, the whole page turned blank. On the Samsung device, typed text ended up in the field in reverse order once the keyboard was closed. On smaller screens nothing of the page remains visible. The keyboard app makes no difference: we reproduced it with SwiftKey and with Gboard.

    Reproduction:

    • @capacitor/android 8.5.1, viewport-fit=cover, default insetsHandling (css), no Keyboard plugin config.
    • Emulator image system-images;android-29;google_apis_playstore;arm64-v8a. The stock image ships WebView 91, so the passthrough branch is not taken until you update "Android System WebView" via the Play Store (we got 154).
    • Logged inside the insets listener while focusing an input: imeVisible=true imeBottom=817 while the parent shrinks from 2065 to 1380 px (adjustResize). The extra setPadding(0, 0, 0, imeInsets.bottom) then removes the keyboard height a second time.
    • With the screen reduced to 1080x1800 (adb shell wm size), the entire page disappears, which matches the field reports.
    • Not reproducible on API 33 (WebView 140 and 153) or on Android 17 (WebView 154).

    Fix: We gate the IME padding in the passthrough branch on Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM (same gate as #8528) and ship it via patch-package. Verified on the API 29 emulator with SwiftKey and Gboard; behaviour on Android 17 is unchanged. #8606 (gate at API 30) should fix the API 29 case just as well.

    Note: @capacitor/android 8.5.3 still contains the unguarded line, so this is not fixed in the latest release yet. It would be great to get #8606 (or an equivalent fix) merged.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions