diff --git a/packages/webview_flutter/CHANGELOG.md b/packages/webview_flutter/CHANGELOG.md
index eb7b1bc4684f..6e7f498968f0 100644
--- a/packages/webview_flutter/CHANGELOG.md
+++ b/packages/webview_flutter/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 0.3.11+1
+
+* Work around a bug in old Android WebView versions that was causing a crash
+ when resizing the webview on old devices.
+
## 0.3.11
* Add an initialAutoMediaPlaybackPolicy setting for controlling how auto media
diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/DisplayListenerProxy.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/DisplayListenerProxy.java
new file mode 100644
index 000000000000..9335f6f37fcf
--- /dev/null
+++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/DisplayListenerProxy.java
@@ -0,0 +1,142 @@
+package io.flutter.plugins.webviewflutter;
+
+import static android.hardware.display.DisplayManager.DisplayListener;
+
+import android.annotation.TargetApi;
+import android.hardware.display.DisplayManager;
+import android.os.Build;
+import android.util.Log;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+
+/**
+ * Works around an Android WebView bug by filtering some DisplayListener invocations.
+ *
+ *
Older Android WebView versions had assumed that when {@link DisplayListener#onDisplayChanged}
+ * is invoked, the display ID it is provided is of a valid display. However it turns out that when a
+ * display is removed Android may call onDisplayChanged with the ID of the removed display, in this
+ * case the Android WebView code tries to fetch and use the display with this ID and crashes with an
+ * NPE.
+ *
+ *
This issue was fixed in the Android WebView code in
+ * https://chromium-review.googlesource.com/517913 which is available starting WebView version
+ * 58.0.3029.125 however older webviews in the wild still have this issue.
+ *
+ *
Since Flutter removes virtual displays whenever a platform view is resized the webview crash
+ * is more likely to happen than other apps. And users were reporting this issue see:
+ * https://github.com/flutter/flutter/issues/30420
+ *
+ *
This class works around the webview bug by unregistering the WebView's DisplayListener, and
+ * instead registering its own DisplayListener which delegates the callbacks to the WebView's
+ * listener unless it's a onDisplayChanged for an invalid display.
+ *
+ *
I did not find a clean way to get a handle of the WebView's DisplayListener so I'm using
+ * reflection to fetch all registered listeners before and after initializing a webview. In the
+ * first initialization of a webview within the process the difference between the lists is the
+ * webview's display listener.
+ */
+@TargetApi(Build.VERSION_CODES.KITKAT)
+class DisplayListenerProxy {
+ private static final String TAG = "DisplayListenerProxy";
+
+ private ArrayList listenersBeforeWebView;
+
+ /** Should be called prior to the webview's initialization. */
+ void onPreWebViewInitialization(DisplayManager displayManager) {
+ listenersBeforeWebView = yoinkDisplayListeners(displayManager);
+ }
+
+ /** Should be called after the webview's initialization. */
+ void onPostWebViewInitialization(final DisplayManager displayManager) {
+ final ArrayList webViewListeners = yoinkDisplayListeners(displayManager);
+ // We recorded the list of listeners prior to initializing webview, any new listeners we see
+ // after initializing the webview are listeners added by the webview.
+ webViewListeners.removeAll(listenersBeforeWebView);
+
+ if (webViewListeners.isEmpty()) {
+ // The Android WebView registers a single display listener per process (even if there
+ // are multiple WebView instances) so this list is expected to be non-empty only the
+ // first time a webview is initialized.
+ // Note that in an add2app scenario if the application had instantiated a non Flutter
+ // WebView prior to instantiating the Flutter WebView we are not able to get a reference
+ // to the WebView's display listener and can't work around the bug.
+ //
+ // This means that webview resizes in add2app Flutter apps with a non Flutter WebView
+ // running on a system with a webview prior to 58.0.3029.125 may crash (the Android's
+ // behavior seems to be racy so it doesn't always happen).
+ return;
+ }
+
+ for (DisplayListener webViewListener : webViewListeners) {
+ // Note that while DisplayManager.unregisterDisplayListener throws when given an
+ // unregistered listener, this isn't an issue as the WebView code never calls
+ // unregisterDisplayListener.
+ displayManager.unregisterDisplayListener(webViewListener);
+
+ // We never explicitly unregister this listener as the webview's listener is never
+ // unregistered (it's released when the process is terminated).
+ displayManager.registerDisplayListener(
+ new DisplayListener() {
+ @Override
+ public void onDisplayAdded(int displayId) {
+ for (DisplayListener webViewListener : webViewListeners) {
+ webViewListener.onDisplayAdded(displayId);
+ }
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ for (DisplayListener webViewListener : webViewListeners) {
+ webViewListener.onDisplayRemoved(displayId);
+ }
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ if (displayManager.getDisplay(displayId) == null) {
+ return;
+ }
+ for (DisplayListener webViewListener : webViewListeners) {
+ webViewListener.onDisplayChanged(displayId);
+ }
+ }
+ },
+ null);
+ }
+ }
+
+ @SuppressWarnings({"unchecked", "PrivateApi"})
+ private static ArrayList yoinkDisplayListeners(DisplayManager displayManager) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
+ // We cannot use reflection on Android O, but it shouldn't matter as it shipped
+ // with a WebView version that has the bug this code is working around fixed.
+ return new ArrayList<>();
+ }
+ try {
+ Field displayManagerGlobalField = DisplayManager.class.getDeclaredField("mGlobal");
+ displayManagerGlobalField.setAccessible(true);
+ Object displayManagerGlobal = displayManagerGlobalField.get(displayManager);
+ Field displayListenersField =
+ displayManagerGlobal.getClass().getDeclaredField("mDisplayListeners");
+ displayListenersField.setAccessible(true);
+ ArrayList