Skip to content

iOS 17.2+: scroll stuck before the bottom after dismissing the keyboard (stale negative contentInset, regression of #1947) #2859

Description

@Manuito83

Is there an existing issue for this?

  • I have searched the existing issues

Current Behavior

On iOS 17.2+, after the on-screen keyboard has been shown and dismissed once, the WKWebView can no longer be scrolled all the way to the bottom: it rubber-bands/bounces back before reaching the end. A relayout (rotation, navigating away and back) temporarily clears it, so it looks intermittent.

Root cause: the #1947 fix (commit e74cde0, "updated fix for #1947 when resizeToAvoidBottomInset is false") added keyboard observers, gated to iOS 17.2+. keyboardWillShow sets a negative scrollView.contentInset to absorb the keyboard space, but keyboardWillHide only resets the guard flag and never restores the inset:

@objc func keyboardWillHide(notification: NSNotification) {
    _scrollViewContentInsetAdjusted = false
}

So the negative contentInset.bottom stays after the keyboard hides, leaving a negative adjusted bottom inset, and the scroll view behaves as if the content ends short.

Expected Behavior

After the keyboard is dismissed, scrollView.contentInset should be restored so the page scrolls normally all the way to the bottom, with no bounce/rubber-band before the end.

Steps with code example to reproduce

Steps with code example to reproduce

Verified on the iOS Simulator (iOS 26.2). The key trigger is a host Scaffold with
resizeToAvoidBottomInset: false: that routes the keyboard inset to the WKWebView
scrollView, so keyboardWillShow takes the negative-contentInset branch. With
resizeToAvoidBottomInset: true Flutter absorbs the keyboard and the bug does not appear.

import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';

void main() => runApp(const MaterialApp(home: ReproPage()));

class ReproPage extends StatelessWidget {
  const ReproPage({super.key});
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      resizeToAvoidBottomInset: false, // <-- key trigger
      body: SafeArea(
        child: InAppWebView(
          initialData: InAppWebViewInitialData(data: '''
            <html><body style="margin:0">
              <input type="text" placeholder="tap me, then dismiss the keyboard" style="font-size:16px;width:100%">
              <div style="height:2600px;background:linear-gradient(#fff,#08f)"></div>
              <div style="padding:24px">BOTTOM OF THE PAGE</div>
            </body></html>
          '''),
        ),
      ),
    );
  }
}
  1. Run on iOS 17.2+ (verified on the iOS Simulator, iOS 26.2).
  2. Tap the input so the keyboard appears, then dismiss it.
  3. Try to scroll to the bottom: the page bounces back before reaching "BOTTOM OF THE PAGE".
  4. Sanity check: flip the Scaffold to resizeToAvoidBottomInset: true and repeat — the bug is gone, confirming the trigger.

Stacktrace/Logs

Stacktrace/Logs
No crash and no logs: this is a layout/scroll-inset issue, not an exception.
After keyboardWillHide, scrollView.contentInset.bottom remains the negative value
set by keyboardWillShow, so scrollView.adjustedContentInset.bottom ends up negative.

Flutter version

v3.44

Operating System, Device-specific and/or Tool

iOS 17.2+ (reproduced on the iOS Simulator, iOS 26.2)

Plugin version

6.2.0-beta.2 / 6.2.0-beta.3 (flutter_inappwebview_ios 1.2.0-beta.x)

Additional information

Regression of #1947. A fix PR is ready: restore the inset in keyboardWillHide the same way the frame setter does, dispatched async so the keyboard is gone and adjustedContentInset is accurate.

Self grab

  • I'm ready to work on this issue!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions