[webview_flutter_android] Updates Android implementation to deallocate native resources automatically (#6428)

diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md
index 910d20d..ffe7c96 100644
--- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md
+++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 2.10.2
+
+* Updates internal code to release native objects automatically with InstanceManager.
+
 ## 2.10.1
 
 * Adds a method to the `WebView` wrapper to retrieve the X and Y positions simultaneously.
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/DownloadListenerFlutterApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/DownloadListenerFlutterApiImpl.java
index 1981d8e..0d4797e 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/DownloadListenerFlutterApiImpl.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/DownloadListenerFlutterApiImpl.java
@@ -47,20 +47,6 @@
         callback);
   }
 
-  /**
-   * Communicates to Dart that the reference to a {@link DownloadListener} was removed.
-   *
-   * @param downloadListener the instance whose reference will be removed
-   * @param callback reply callback with return value from Dart
-   */
-  public void dispose(DownloadListener downloadListener, Reply<Void> callback) {
-    if (instanceManager.containsInstance(downloadListener)) {
-      dispose(getIdentifierForListener(downloadListener), callback);
-    } else {
-      callback.reply(null);
-    }
-  }
-
   private long getIdentifierForListener(DownloadListener listener) {
     final Long identifier = instanceManager.getIdentifierForStrongReference(listener);
     if (identifier == null) {
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/DownloadListenerHostApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/DownloadListenerHostApiImpl.java
index ed0c2ae..a9cbcbd 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/DownloadListenerHostApiImpl.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/DownloadListenerHostApiImpl.java
@@ -6,7 +6,6 @@
 
 import android.webkit.DownloadListener;
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.DownloadListenerHostApi;
 
 /**
@@ -21,11 +20,9 @@
 
   /**
    * Implementation of {@link DownloadListener} that passes arguments of callback methods to Dart.
-   *
-   * <p>No messages are sent to Dart after {@link DownloadListenerImpl#release} is called.
    */
-  public static class DownloadListenerImpl implements DownloadListener, Releasable {
-    @Nullable private DownloadListenerFlutterApiImpl flutterApi;
+  public static class DownloadListenerImpl implements DownloadListener {
+    private final DownloadListenerFlutterApiImpl flutterApi;
 
     /**
      * Creates a {@link DownloadListenerImpl} that passes arguments of callbacks methods to Dart.
@@ -43,18 +40,8 @@
         String contentDisposition,
         String mimetype,
         long contentLength) {
-      if (flutterApi != null) {
-        flutterApi.onDownloadStart(
-            this, url, userAgent, contentDisposition, mimetype, contentLength, reply -> {});
-      }
-    }
-
-    @Override
-    public void release() {
-      if (flutterApi != null) {
-        flutterApi.dispose(this, reply -> {});
-      }
-      flutterApi = null;
+      flutterApi.onDownloadStart(
+          this, url, userAgent, contentDisposition, mimetype, contentLength, reply -> {});
     }
   }
 
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java
index 9a6b1c4..af096bf 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java
@@ -543,8 +543,6 @@
   public interface WebViewHostApi {
     void create(@NonNull Long instanceId, @NonNull Boolean useHybridComposition);
 
-    void dispose(@NonNull Long instanceId);
-
     void loadData(
         @NonNull Long instanceId,
         @NonNull String data,
@@ -656,31 +654,6 @@
       {
         BasicMessageChannel<Object> channel =
             new BasicMessageChannel<>(
-                binaryMessenger, "dev.flutter.pigeon.WebViewHostApi.dispose", getCodec());
-        if (api != null) {
-          channel.setMessageHandler(
-              (message, reply) -> {
-                Map<String, Object> wrapped = new HashMap<>();
-                try {
-                  ArrayList<Object> args = (ArrayList<Object>) message;
-                  Number instanceIdArg = (Number) args.get(0);
-                  if (instanceIdArg == null) {
-                    throw new NullPointerException("instanceIdArg unexpectedly null.");
-                  }
-                  api.dispose((instanceIdArg == null) ? null : instanceIdArg.longValue());
-                  wrapped.put("result", null);
-                } catch (Error | RuntimeException exception) {
-                  wrapped.put("error", wrapError(exception));
-                }
-                reply.reply(wrapped);
-              });
-        } else {
-          channel.setMessageHandler(null);
-        }
-      }
-      {
-        BasicMessageChannel<Object> channel =
-            new BasicMessageChannel<>(
                 binaryMessenger, "dev.flutter.pigeon.WebViewHostApi.loadData", getCodec());
         if (api != null) {
           channel.setMessageHandler(
@@ -1469,8 +1442,6 @@
   public interface WebSettingsHostApi {
     void create(@NonNull Long instanceId, @NonNull Long webViewInstanceId);
 
-    void dispose(@NonNull Long instanceId);
-
     void setDomStorageEnabled(@NonNull Long instanceId, @NonNull Boolean flag);
 
     void setJavaScriptCanOpenWindowsAutomatically(@NonNull Long instanceId, @NonNull Boolean flag);
@@ -1538,31 +1509,6 @@
       {
         BasicMessageChannel<Object> channel =
             new BasicMessageChannel<>(
-                binaryMessenger, "dev.flutter.pigeon.WebSettingsHostApi.dispose", getCodec());
-        if (api != null) {
-          channel.setMessageHandler(
-              (message, reply) -> {
-                Map<String, Object> wrapped = new HashMap<>();
-                try {
-                  ArrayList<Object> args = (ArrayList<Object>) message;
-                  Number instanceIdArg = (Number) args.get(0);
-                  if (instanceIdArg == null) {
-                    throw new NullPointerException("instanceIdArg unexpectedly null.");
-                  }
-                  api.dispose((instanceIdArg == null) ? null : instanceIdArg.longValue());
-                  wrapped.put("result", null);
-                } catch (Error | RuntimeException exception) {
-                  wrapped.put("error", wrapError(exception));
-                }
-                reply.reply(wrapped);
-              });
-        } else {
-          channel.setMessageHandler(null);
-        }
-      }
-      {
-        BasicMessageChannel<Object> channel =
-            new BasicMessageChannel<>(
                 binaryMessenger,
                 "dev.flutter.pigeon.WebSettingsHostApi.setDomStorageEnabled",
                 getCodec());
@@ -2022,19 +1968,6 @@
       return JavaScriptChannelFlutterApiCodec.INSTANCE;
     }
 
-    public void dispose(@NonNull Long instanceIdArg, Reply<Void> callback) {
-      BasicMessageChannel<Object> channel =
-          new BasicMessageChannel<>(
-              binaryMessenger,
-              "dev.flutter.pigeon.JavaScriptChannelFlutterApi.dispose",
-              getCodec());
-      channel.send(
-          new ArrayList<Object>(Arrays.asList(instanceIdArg)),
-          channelReply -> {
-            callback.reply(null);
-          });
-    }
-
     public void postMessage(
         @NonNull Long instanceIdArg, @NonNull String messageArg, Reply<Void> callback) {
       BasicMessageChannel<Object> channel =
@@ -2154,17 +2087,6 @@
       return WebViewClientFlutterApiCodec.INSTANCE;
     }
 
-    public void dispose(@NonNull Long instanceIdArg, Reply<Void> callback) {
-      BasicMessageChannel<Object> channel =
-          new BasicMessageChannel<>(
-              binaryMessenger, "dev.flutter.pigeon.WebViewClientFlutterApi.dispose", getCodec());
-      channel.send(
-          new ArrayList<Object>(Arrays.asList(instanceIdArg)),
-          channelReply -> {
-            callback.reply(null);
-          });
-    }
-
     public void onPageStarted(
         @NonNull Long instanceIdArg,
         @NonNull Long webViewInstanceIdArg,
@@ -2347,17 +2269,6 @@
       return DownloadListenerFlutterApiCodec.INSTANCE;
     }
 
-    public void dispose(@NonNull Long instanceIdArg, Reply<Void> callback) {
-      BasicMessageChannel<Object> channel =
-          new BasicMessageChannel<>(
-              binaryMessenger, "dev.flutter.pigeon.DownloadListenerFlutterApi.dispose", getCodec());
-      channel.send(
-          new ArrayList<Object>(Arrays.asList(instanceIdArg)),
-          channelReply -> {
-            callback.reply(null);
-          });
-    }
-
     public void onDownloadStart(
         @NonNull Long instanceIdArg,
         @NonNull String urlArg,
@@ -2394,7 +2305,7 @@
 
   /** Generated interface from Pigeon that represents a handler of messages from Flutter. */
   public interface WebChromeClientHostApi {
-    void create(@NonNull Long instanceId, @NonNull Long webViewClientInstanceId);
+    void create(@NonNull Long instanceId);
 
     /** The codec used by WebChromeClientHostApi. */
     static MessageCodec<Object> getCodec() {
@@ -2420,15 +2331,7 @@
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
                   }
-                  Number webViewClientInstanceIdArg = (Number) args.get(1);
-                  if (webViewClientInstanceIdArg == null) {
-                    throw new NullPointerException("webViewClientInstanceIdArg unexpectedly null.");
-                  }
-                  api.create(
-                      (instanceIdArg == null) ? null : instanceIdArg.longValue(),
-                      (webViewClientInstanceIdArg == null)
-                          ? null
-                          : webViewClientInstanceIdArg.longValue());
+                  api.create((instanceIdArg == null) ? null : instanceIdArg.longValue());
                   wrapped.put("result", null);
                 } catch (Error | RuntimeException exception) {
                   wrapped.put("error", wrapError(exception));
@@ -2545,17 +2448,6 @@
       return WebChromeClientFlutterApiCodec.INSTANCE;
     }
 
-    public void dispose(@NonNull Long instanceIdArg, Reply<Void> callback) {
-      BasicMessageChannel<Object> channel =
-          new BasicMessageChannel<>(
-              binaryMessenger, "dev.flutter.pigeon.WebChromeClientFlutterApi.dispose", getCodec());
-      channel.send(
-          new ArrayList<Object>(Arrays.asList(instanceIdArg)),
-          channelReply -> {
-            callback.reply(null);
-          });
-    }
-
     public void onProgressChanged(
         @NonNull Long instanceIdArg,
         @NonNull Long webViewInstanceIdArg,
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/JavaScriptChannel.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/JavaScriptChannel.java
index ce6f2b8..cf2c262 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/JavaScriptChannel.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/JavaScriptChannel.java
@@ -8,7 +8,6 @@
 import android.os.Looper;
 import android.webkit.JavascriptInterface;
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 
 /**
  * Added as a JavaScript interface to the WebView for any JavaScript channel that the Dart code sets
@@ -16,13 +15,11 @@
  *
  * <p>Exposes a single method named `postMessage` to JavaScript, which sends a message to the Dart
  * code.
- *
- * <p>No messages are sent to Dart after {@link JavaScriptChannel#release} is called.
  */
-public class JavaScriptChannel implements Releasable {
+public class JavaScriptChannel {
   private final Handler platformThreadHandler;
   final String javaScriptChannelName;
-  @Nullable private JavaScriptChannelFlutterApiImpl flutterApi;
+  private final JavaScriptChannelFlutterApiImpl flutterApi;
 
   /**
    * Creates a {@link JavaScriptChannel} that passes arguments of callback methods to Dart.
@@ -46,9 +43,7 @@
   public void postMessage(final String message) {
     final Runnable postMessageRunnable =
         () -> {
-          if (flutterApi != null) {
-            flutterApi.postMessage(JavaScriptChannel.this, message, reply -> {});
-          }
+          flutterApi.postMessage(JavaScriptChannel.this, message, reply -> {});
         };
 
     if (platformThreadHandler.getLooper() == Looper.myLooper()) {
@@ -57,12 +52,4 @@
       platformThreadHandler.post(postMessageRunnable);
     }
   }
-
-  @Override
-  public void release() {
-    if (flutterApi != null) {
-      flutterApi.dispose(this, reply -> {});
-    }
-    flutterApi = null;
-  }
 }
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/JavaScriptChannelFlutterApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/JavaScriptChannelFlutterApiImpl.java
index dbac833..ca08926 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/JavaScriptChannelFlutterApiImpl.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/JavaScriptChannelFlutterApiImpl.java
@@ -33,20 +33,6 @@
     super.postMessage(getIdentifierForJavaScriptChannel(javaScriptChannel), messageArg, callback);
   }
 
-  /**
-   * Communicates to Dart that the reference to a {@link JavaScriptChannel} was removed.
-   *
-   * @param javaScriptChannel The instance whose reference will be removed.
-   * @param callback Reply callback with return value from Dart.
-   */
-  public void dispose(JavaScriptChannel javaScriptChannel, Reply<Void> callback) {
-    if (instanceManager.containsInstance(javaScriptChannel)) {
-      dispose(getIdentifierForJavaScriptChannel(javaScriptChannel), callback);
-    } else {
-      callback.reply(null);
-    }
-  }
-
   private long getIdentifierForJavaScriptChannel(JavaScriptChannel javaScriptChannel) {
     final Long identifier = instanceManager.getIdentifierForStrongReference(javaScriptChannel);
     if (identifier == null) {
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/Releasable.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/Releasable.java
deleted file mode 100644
index 9c4ed76..0000000
--- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/Releasable.java
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package io.flutter.plugins.webviewflutter;
-
-/**
- * Represents a resource, or a holder of resources, which may be released once they are no longer
- * needed.
- */
-interface Releasable {
-  /** Notify that that the reference to an object will be removed by a holder. */
-  void release();
-}
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebChromeClientFlutterApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebChromeClientFlutterApiImpl.java
index 28d63ec..cf263e2 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebChromeClientFlutterApiImpl.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebChromeClientFlutterApiImpl.java
@@ -40,20 +40,6 @@
         getIdentifierForClient(webChromeClient), webViewIdentifier, progress, callback);
   }
 
-  /**
-   * Communicates to Dart that the reference to a {@link WebChromeClient}} was removed.
-   *
-   * @param webChromeClient the instance whose reference will be removed
-   * @param callback reply callback with return value from Dart
-   */
-  public void dispose(WebChromeClient webChromeClient, Reply<Void> callback) {
-    if (instanceManager.containsInstance(webChromeClient)) {
-      dispose(getIdentifierForClient(webChromeClient), callback);
-    } else {
-      callback.reply(null);
-    }
-  }
-
   private long getIdentifierForClient(WebChromeClient webChromeClient) {
     final Long identifier = instanceManager.getIdentifierForStrongReference(webChromeClient);
     if (identifier == null) {
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebChromeClientHostApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebChromeClientHostApiImpl.java
index 0f50c82..7b5241e 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebChromeClientHostApiImpl.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebChromeClientHostApiImpl.java
@@ -29,20 +29,17 @@
   /**
    * Implementation of {@link WebChromeClient} that passes arguments of callback methods to Dart.
    */
-  public static class WebChromeClientImpl extends WebChromeClient implements Releasable {
-    @Nullable private WebChromeClientFlutterApiImpl flutterApi;
-    private WebViewClient webViewClient;
+  public static class WebChromeClientImpl extends WebChromeClient {
+    private final WebChromeClientFlutterApiImpl flutterApi;
+    @Nullable private WebViewClient webViewClient;
 
     /**
      * Creates a {@link WebChromeClient} that passes arguments of callbacks methods to Dart.
      *
      * @param flutterApi handles sending messages to Dart
-     * @param webViewClient receives forwarded calls from {@link WebChromeClient#onCreateWindow}
      */
-    public WebChromeClientImpl(
-        @NonNull WebChromeClientFlutterApiImpl flutterApi, WebViewClient webViewClient) {
+    public WebChromeClientImpl(@NonNull WebChromeClientFlutterApiImpl flutterApi) {
       this.flutterApi = flutterApi;
-      this.webViewClient = webViewClient;
     }
 
     @Override
@@ -67,6 +64,14 @@
     @VisibleForTesting
     boolean onCreateWindow(
         final WebView view, Message resultMsg, @Nullable WebView onCreateWindowWebView) {
+      // WebChromeClient requires a WebViewClient because of a bug fix that makes
+      // calls to WebViewClient.requestLoading/WebViewClient.urlLoading when a new
+      // window is opened. This is to make sure a url opened by `Window.open` has
+      // a secure url.
+      if (webViewClient == null) {
+        return false;
+      }
+
       final WebViewClient windowWebViewClient =
           new WebViewClient() {
             @RequiresApi(api = Build.VERSION_CODES.N)
@@ -102,9 +107,7 @@
 
     @Override
     public void onProgressChanged(WebView view, int progress) {
-      if (flutterApi != null) {
-        flutterApi.onProgressChanged(this, view, (long) progress, reply -> {});
-      }
+      flutterApi.onProgressChanged(this, view, (long) progress, reply -> {});
     }
 
     /**
@@ -113,17 +116,9 @@
      *
      * @param webViewClient the forwarding {@link WebViewClient}
      */
-    public void setWebViewClient(WebViewClient webViewClient) {
+    public void setWebViewClient(@NonNull WebViewClient webViewClient) {
       this.webViewClient = webViewClient;
     }
-
-    @Override
-    public void release() {
-      if (flutterApi != null) {
-        flutterApi.dispose(this, reply -> {});
-      }
-      flutterApi = null;
-    }
   }
 
   /** Handles creating {@link WebChromeClient}s for a {@link WebChromeClientHostApiImpl}. */
@@ -132,12 +127,10 @@
      * Creates a {@link DownloadListenerHostApiImpl.DownloadListenerImpl}.
      *
      * @param flutterApi handles sending messages to Dart
-     * @param webViewClient receives forwarded calls from {@link WebChromeClient#onCreateWindow}
-     * @return the created {@link DownloadListenerHostApiImpl.DownloadListenerImpl}
+     * @return the created {@link WebChromeClientHostApiImpl.WebChromeClientImpl}
      */
-    public WebChromeClientImpl createWebChromeClient(
-        WebChromeClientFlutterApiImpl flutterApi, WebViewClient webViewClient) {
-      return new WebChromeClientImpl(flutterApi, webViewClient);
+    public WebChromeClientImpl createWebChromeClient(WebChromeClientFlutterApiImpl flutterApi) {
+      return new WebChromeClientImpl(flutterApi);
     }
   }
 
@@ -158,11 +151,9 @@
   }
 
   @Override
-  public void create(Long instanceId, Long webViewClientInstanceId) {
-    final WebViewClient webViewClient =
-        (WebViewClient) instanceManager.getInstance(webViewClientInstanceId);
+  public void create(Long instanceId) {
     final WebChromeClient webChromeClient =
-        webChromeClientCreator.createWebChromeClient(flutterApi, webViewClient);
+        webChromeClientCreator.createWebChromeClient(flutterApi);
     instanceManager.addDartCreatedInstance(webChromeClient, instanceId);
   }
 }
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebSettingsHostApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebSettingsHostApiImpl.java
index 5b6f9e7..98fd4fc 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebSettingsHostApiImpl.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebSettingsHostApiImpl.java
@@ -50,11 +50,6 @@
   }
 
   @Override
-  public void dispose(Long instanceId) {
-    instanceManager.remove(instanceId);
-  }
-
-  @Override
   public void setDomStorageEnabled(Long instanceId, Boolean flag) {
     final WebSettings webSettings = (WebSettings) instanceManager.getInstance(instanceId);
     webSettings.setDomStorageEnabled(flag);
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewClientFlutterApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewClientFlutterApiImpl.java
index c23e8e7..0dc0bbb 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewClientFlutterApiImpl.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewClientFlutterApiImpl.java
@@ -197,20 +197,6 @@
     urlLoading(getIdentifierForClient(webViewClient), webViewIdentifier, urlArg, callback);
   }
 
-  /**
-   * Communicates to Dart that the reference to a {@link WebViewClient} was removed.
-   *
-   * @param webViewClient the instance whose reference will be removed
-   * @param callback reply callback with return value from Dart
-   */
-  public void dispose(WebViewClient webViewClient, Reply<Void> callback) {
-    if (instanceManager.containsInstance(webViewClient)) {
-      dispose(getIdentifierForClient(webViewClient), callback);
-    } else {
-      callback.reply(null);
-    }
-  }
-
   private long getIdentifierForClient(WebViewClient webViewClient) {
     final Long identifier = instanceManager.getIdentifierForStrongReference(webViewClient);
     if (identifier == null) {
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewClientHostApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewClientHostApiImpl.java
index 4833ee9..fd08cfa 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewClientHostApiImpl.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewClientHostApiImpl.java
@@ -14,7 +14,6 @@
 import android.webkit.WebView;
 import android.webkit.WebViewClient;
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
 import androidx.webkit.WebResourceErrorCompat;
 import androidx.webkit.WebViewClientCompat;
@@ -29,15 +28,10 @@
   private final WebViewClientCreator webViewClientCreator;
   private final WebViewClientFlutterApiImpl flutterApi;
 
-  /**
-   * An interface implemented by a class that extends {@link WebViewClient} and {@link Releasable}.
-   */
-  public interface ReleasableWebViewClient extends Releasable {}
-
   /** Implementation of {@link WebViewClient} that passes arguments of callback methods to Dart. */
   @RequiresApi(Build.VERSION_CODES.N)
-  public static class WebViewClientImpl extends WebViewClient implements ReleasableWebViewClient {
-    @Nullable private WebViewClientFlutterApiImpl flutterApi;
+  public static class WebViewClientImpl extends WebViewClient {
+    private final WebViewClientFlutterApiImpl flutterApi;
     private final boolean shouldOverrideUrlLoading;
 
     /**
@@ -54,47 +48,35 @@
 
     @Override
     public void onPageStarted(WebView view, String url, Bitmap favicon) {
-      if (flutterApi != null) {
-        flutterApi.onPageStarted(this, view, url, reply -> {});
-      }
+      flutterApi.onPageStarted(this, view, url, reply -> {});
     }
 
     @Override
     public void onPageFinished(WebView view, String url) {
-      if (flutterApi != null) {
-        flutterApi.onPageFinished(this, view, url, reply -> {});
-      }
+      flutterApi.onPageFinished(this, view, url, reply -> {});
     }
 
     @Override
     public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
-      if (flutterApi != null) {
-        flutterApi.onReceivedRequestError(this, view, request, error, reply -> {});
-      }
+      flutterApi.onReceivedRequestError(this, view, request, error, reply -> {});
     }
 
     @Override
     public void onReceivedError(
         WebView view, int errorCode, String description, String failingUrl) {
-      if (flutterApi != null) {
-        flutterApi.onReceivedError(
-            this, view, (long) errorCode, description, failingUrl, reply -> {});
-      }
+      flutterApi.onReceivedError(
+          this, view, (long) errorCode, description, failingUrl, reply -> {});
     }
 
     @Override
     public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
-      if (flutterApi != null) {
-        flutterApi.requestLoading(this, view, request, reply -> {});
-      }
+      flutterApi.requestLoading(this, view, request, reply -> {});
       return shouldOverrideUrlLoading;
     }
 
     @Override
     public boolean shouldOverrideUrlLoading(WebView view, String url) {
-      if (flutterApi != null) {
-        flutterApi.urlLoading(this, view, url, reply -> {});
-      }
+      flutterApi.urlLoading(this, view, url, reply -> {});
       return shouldOverrideUrlLoading;
     }
 
@@ -104,22 +86,14 @@
       // handled even though they were handled. We don't want to propagate those as they're not
       // truly lost.
     }
-
-    public void release() {
-      if (flutterApi != null) {
-        flutterApi.dispose(this, reply -> {});
-      }
-      flutterApi = null;
-    }
   }
 
   /**
    * Implementation of {@link WebViewClientCompat} that passes arguments of callback methods to
    * Dart.
    */
-  public static class WebViewClientCompatImpl extends WebViewClientCompat
-      implements ReleasableWebViewClient {
-    private @Nullable WebViewClientFlutterApiImpl flutterApi;
+  public static class WebViewClientCompatImpl extends WebViewClientCompat {
+    private final WebViewClientFlutterApiImpl flutterApi;
     private final boolean shouldOverrideUrlLoading;
 
     public WebViewClientCompatImpl(
@@ -130,16 +104,12 @@
 
     @Override
     public void onPageStarted(WebView view, String url, Bitmap favicon) {
-      if (flutterApi != null) {
-        flutterApi.onPageStarted(this, view, url, reply -> {});
-      }
+      flutterApi.onPageStarted(this, view, url, reply -> {});
     }
 
     @Override
     public void onPageFinished(WebView view, String url) {
-      if (flutterApi != null) {
-        flutterApi.onPageFinished(this, view, url, reply -> {});
-      }
+      flutterApi.onPageFinished(this, view, url, reply -> {});
     }
 
     // This method is only called when the WebViewFeature.RECEIVE_WEB_RESOURCE_ERROR feature is
@@ -151,35 +121,27 @@
         @NonNull WebView view,
         @NonNull WebResourceRequest request,
         @NonNull WebResourceErrorCompat error) {
-      if (flutterApi != null) {
-        flutterApi.onReceivedRequestError(this, view, request, error, reply -> {});
-      }
+      flutterApi.onReceivedRequestError(this, view, request, error, reply -> {});
     }
 
     @Override
     public void onReceivedError(
         WebView view, int errorCode, String description, String failingUrl) {
-      if (flutterApi != null) {
-        flutterApi.onReceivedError(
-            this, view, (long) errorCode, description, failingUrl, reply -> {});
-      }
+      flutterApi.onReceivedError(
+          this, view, (long) errorCode, description, failingUrl, reply -> {});
     }
 
     @TargetApi(Build.VERSION_CODES.LOLLIPOP)
     @Override
     public boolean shouldOverrideUrlLoading(
         @NonNull WebView view, @NonNull WebResourceRequest request) {
-      if (flutterApi != null) {
-        flutterApi.requestLoading(this, view, request, reply -> {});
-      }
+      flutterApi.requestLoading(this, view, request, reply -> {});
       return shouldOverrideUrlLoading;
     }
 
     @Override
     public boolean shouldOverrideUrlLoading(WebView view, String url) {
-      if (flutterApi != null) {
-        flutterApi.urlLoading(this, view, url, reply -> {});
-      }
+      flutterApi.urlLoading(this, view, url, reply -> {});
       return shouldOverrideUrlLoading;
     }
 
@@ -189,13 +151,6 @@
       // handled even though they were handled. We don't want to propagate those as they're not
       // truly lost.
     }
-
-    public void release() {
-      if (flutterApi != null) {
-        flutterApi.dispose(this, reply -> {});
-      }
-      flutterApi = null;
-    }
   }
 
   /** Handles creating {@link WebViewClient}s for a {@link WebViewClientHostApiImpl}. */
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java
index 8db976a..61d18b1 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewFlutterPlugin.java
@@ -16,6 +16,7 @@
 import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.CookieManagerHostApi;
 import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.DownloadListenerHostApi;
 import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.FlutterAssetManagerHostApi;
+import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.JavaObjectHostApi;
 import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.JavaScriptChannelHostApi;
 import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebChromeClientHostApi;
 import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebSettingsHostApi;
@@ -76,15 +77,22 @@
       Context context,
       View containerView,
       FlutterAssetManager flutterAssetManager) {
-
-    instanceManager = InstanceManager.open(identifier -> {});
+    instanceManager =
+        InstanceManager.open(
+            identifier ->
+                new GeneratedAndroidWebView.JavaObjectFlutterApi(binaryMessenger)
+                    .dispose(identifier, reply -> {}));
 
     viewRegistry.registerViewFactory(
         "plugins.flutter.io/webview", new FlutterWebViewFactory(instanceManager));
 
     webViewHostApi =
         new WebViewHostApiImpl(
-            instanceManager, new WebViewHostApiImpl.WebViewProxy(), context, containerView);
+            instanceManager,
+            binaryMessenger,
+            new WebViewHostApiImpl.WebViewProxy(),
+            context,
+            containerView);
     javaScriptChannelHostApi =
         new JavaScriptChannelHostApiImpl(
             instanceManager,
@@ -92,6 +100,7 @@
             new JavaScriptChannelFlutterApiImpl(binaryMessenger, instanceManager),
             new Handler(context.getMainLooper()));
 
+    JavaObjectHostApi.setup(binaryMessenger, new JavaObjectHostApiImpl(instanceManager));
     WebViewHostApi.setup(binaryMessenger, webViewHostApi);
     JavaScriptChannelHostApi.setup(binaryMessenger, javaScriptChannelHostApi);
     WebViewClientHostApi.setup(
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewHostApiImpl.java b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewHostApiImpl.java
index f257ace..c18fb37 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewHostApiImpl.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewHostApiImpl.java
@@ -14,12 +14,10 @@
 import android.webkit.WebViewClient;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import io.flutter.plugin.common.BinaryMessenger;
 import io.flutter.plugin.platform.PlatformView;
-import io.flutter.plugins.webviewflutter.DownloadListenerHostApiImpl.DownloadListenerImpl;
 import io.flutter.plugins.webviewflutter.GeneratedAndroidWebView.WebViewHostApi;
 import io.flutter.plugins.webviewflutter.WebChromeClientHostApiImpl.WebChromeClientImpl;
-import io.flutter.plugins.webviewflutter.WebViewClientHostApiImpl.ReleasableWebViewClient;
-import java.util.HashMap;
 import java.util.Map;
 import java.util.Objects;
 
@@ -33,6 +31,7 @@
   private final WebViewProxy webViewProxy;
   // Only used with WebView using virtual displays.
   @Nullable private final View containerView;
+  private final BinaryMessenger binaryMessenger;
 
   private Context context;
 
@@ -42,10 +41,14 @@
      * Creates a {@link WebViewPlatformView}.
      *
      * @param context an Activity Context to access application assets
+     * @param binaryMessenger used to communicate with Dart over asynchronous messages
+     * @param instanceManager mangages instances used to communicate with the corresponding objects
+     *     in Dart
      * @return the created {@link WebViewPlatformView}
      */
-    public WebViewPlatformView createWebView(Context context) {
-      return new WebViewPlatformView(context);
+    public WebViewPlatformView createWebView(
+        Context context, BinaryMessenger binaryMessenger, InstanceManager instanceManager) {
+      return new WebViewPlatformView(context, binaryMessenger, instanceManager);
     }
 
     /**
@@ -56,8 +59,12 @@
      * @return the created {@link InputAwareWebViewPlatformView}
      */
     public InputAwareWebViewPlatformView createInputAwareWebView(
-        Context context, @Nullable View containerView) {
-      return new InputAwareWebViewPlatformView(context, containerView);
+        Context context,
+        BinaryMessenger binaryMessenger,
+        InstanceManager instanceManager,
+        @Nullable View containerView) {
+      return new InputAwareWebViewPlatformView(
+          context, binaryMessenger, instanceManager, containerView);
     }
 
     /**
@@ -70,51 +77,26 @@
     }
   }
 
-  private static class ReleasableValue<T extends Releasable> {
-    @Nullable private T value;
-
-    ReleasableValue() {}
-
-    ReleasableValue(@Nullable T value) {
-      this.value = value;
-    }
-
-    void set(@Nullable T newValue) {
-      release();
-      value = newValue;
-    }
-
-    @Nullable
-    T get() {
-      return value;
-    }
-
-    void release() {
-      if (value != null) {
-        value.release();
-      }
-      value = null;
-    }
-  }
-
   /** Implementation of {@link WebView} that can be used as a Flutter {@link PlatformView}s. */
-  public static class WebViewPlatformView extends WebView implements PlatformView, Releasable {
-    private final ReleasableValue<WebViewClientHostApiImpl.ReleasableWebViewClient>
-        currentWebViewClient = new ReleasableValue<>();
-    private final ReleasableValue<DownloadListenerImpl> currentDownloadListener =
-        new ReleasableValue<>();
-    private final ReleasableValue<WebChromeClientImpl> currentWebChromeClient =
-        new ReleasableValue<>();
-    private final Map<String, ReleasableValue<JavaScriptChannel>> javaScriptInterfaces =
-        new HashMap<>();
+  public static class WebViewPlatformView extends WebView implements PlatformView {
+    private WebViewClient currentWebViewClient;
+    private WebChromeClientImpl currentWebChromeClient;
 
     /**
      * Creates a {@link WebViewPlatformView}.
      *
      * @param context an Activity Context to access application assets. This value cannot be null.
      */
-    public WebViewPlatformView(Context context) {
+    public WebViewPlatformView(
+        Context context, BinaryMessenger binaryMessenger, InstanceManager instanceManager) {
       super(context);
+      currentWebViewClient = new WebViewClient();
+      currentWebChromeClient =
+          new WebChromeClientImpl(
+              new WebChromeClientFlutterApiImpl(binaryMessenger, instanceManager));
+
+      setWebViewClient(currentWebViewClient);
+      setWebChromeClient(currentWebChromeClient);
     }
 
     @Override
@@ -130,56 +112,18 @@
     @Override
     public void setWebViewClient(WebViewClient webViewClient) {
       super.setWebViewClient(webViewClient);
-      currentWebViewClient.set((ReleasableWebViewClient) webViewClient);
-
-      final WebChromeClientImpl webChromeClient = currentWebChromeClient.get();
-      if (webChromeClient != null) {
-        ((WebChromeClientImpl) webChromeClient).setWebViewClient(webViewClient);
-      }
-    }
-
-    @Override
-    public void setDownloadListener(DownloadListener listener) {
-      super.setDownloadListener(listener);
-      currentDownloadListener.set((DownloadListenerImpl) listener);
+      currentWebViewClient = webViewClient;
+      currentWebChromeClient.setWebViewClient(webViewClient);
     }
 
     @Override
     public void setWebChromeClient(WebChromeClient client) {
       super.setWebChromeClient(client);
-      currentWebChromeClient.set((WebChromeClientImpl) client);
-    }
-
-    @SuppressLint("JavascriptInterface")
-    @Override
-    public void addJavascriptInterface(Object object, String name) {
-      super.addJavascriptInterface(object, name);
-      if (object instanceof JavaScriptChannel) {
-        final ReleasableValue<JavaScriptChannel> javaScriptChannel = javaScriptInterfaces.get(name);
-        if (javaScriptChannel != null && javaScriptChannel.get() != object) {
-          javaScriptChannel.release();
-        }
-        javaScriptInterfaces.put(name, new ReleasableValue<>((JavaScriptChannel) object));
+      if (!(client instanceof WebChromeClientImpl)) {
+        throw new AssertionError("Client must be a WebChromeClientImpl.");
       }
-    }
-
-    @Override
-    public void removeJavascriptInterface(@NonNull String name) {
-      super.removeJavascriptInterface(name);
-      final ReleasableValue<JavaScriptChannel> javaScriptChannel = javaScriptInterfaces.get(name);
-      javaScriptChannel.release();
-      javaScriptInterfaces.remove(name);
-    }
-
-    @Override
-    public void release() {
-      currentWebViewClient.release();
-      currentDownloadListener.release();
-      currentWebChromeClient.release();
-      for (ReleasableValue<JavaScriptChannel> channel : javaScriptInterfaces.values()) {
-        channel.release();
-      }
-      javaScriptInterfaces.clear();
+      currentWebChromeClient = (WebChromeClientImpl) client;
+      currentWebChromeClient.setWebViewClient(currentWebViewClient);
     }
   }
 
@@ -189,23 +133,28 @@
    */
   @SuppressLint("ViewConstructor")
   public static class InputAwareWebViewPlatformView extends InputAwareWebView
-      implements PlatformView, Releasable {
-    private final ReleasableValue<WebViewClientHostApiImpl.ReleasableWebViewClient>
-        currentWebViewClient = new ReleasableValue<>();
-    private final ReleasableValue<DownloadListenerImpl> currentDownloadListener =
-        new ReleasableValue<>();
-    private final ReleasableValue<WebChromeClientImpl> currentWebChromeClient =
-        new ReleasableValue<>();
-    private final Map<String, ReleasableValue<JavaScriptChannel>> javaScriptInterfaces =
-        new HashMap<>();
+      implements PlatformView {
+    private WebViewClient currentWebViewClient;
+    private WebChromeClientImpl currentWebChromeClient;
 
     /**
      * Creates a {@link InputAwareWebViewPlatformView}.
      *
      * @param context an Activity Context to access application assets. This value cannot be null.
      */
-    public InputAwareWebViewPlatformView(Context context, View containerView) {
+    public InputAwareWebViewPlatformView(
+        Context context,
+        BinaryMessenger binaryMessenger,
+        InstanceManager instanceManager,
+        View containerView) {
       super(context, containerView);
+      currentWebViewClient = new WebViewClient();
+      currentWebChromeClient =
+          new WebChromeClientImpl(
+              new WebChromeClientFlutterApiImpl(binaryMessenger, instanceManager));
+
+      setWebViewClient(currentWebViewClient);
+      setWebChromeClient(currentWebChromeClient);
     }
 
     @Override
@@ -242,56 +191,18 @@
     @Override
     public void setWebViewClient(WebViewClient webViewClient) {
       super.setWebViewClient(webViewClient);
-      currentWebViewClient.set((ReleasableWebViewClient) webViewClient);
-
-      final WebChromeClientImpl webChromeClient = currentWebChromeClient.get();
-      if (webChromeClient != null) {
-        webChromeClient.setWebViewClient(webViewClient);
-      }
-    }
-
-    @Override
-    public void setDownloadListener(DownloadListener listener) {
-      super.setDownloadListener(listener);
-      currentDownloadListener.set((DownloadListenerImpl) listener);
+      currentWebViewClient = webViewClient;
+      currentWebChromeClient.setWebViewClient(webViewClient);
     }
 
     @Override
     public void setWebChromeClient(WebChromeClient client) {
       super.setWebChromeClient(client);
-      currentWebChromeClient.set((WebChromeClientImpl) client);
-    }
-
-    @SuppressLint("JavascriptInterface")
-    @Override
-    public void addJavascriptInterface(Object object, String name) {
-      super.addJavascriptInterface(object, name);
-      if (object instanceof JavaScriptChannel) {
-        final ReleasableValue<JavaScriptChannel> javaScriptChannel = javaScriptInterfaces.get(name);
-        if (javaScriptChannel != null && javaScriptChannel.get() != object) {
-          javaScriptChannel.release();
-        }
-        javaScriptInterfaces.put(name, new ReleasableValue<>((JavaScriptChannel) object));
+      if (!(client instanceof WebChromeClientImpl)) {
+        throw new AssertionError("Client must be a WebChromeClientImpl.");
       }
-    }
-
-    @Override
-    public void removeJavascriptInterface(@NonNull String name) {
-      super.removeJavascriptInterface(name);
-      final ReleasableValue<JavaScriptChannel> javaScriptChannel = javaScriptInterfaces.get(name);
-      javaScriptChannel.release();
-      javaScriptInterfaces.remove(name);
-    }
-
-    @Override
-    public void release() {
-      currentWebViewClient.release();
-      currentDownloadListener.release();
-      currentWebChromeClient.release();
-      for (ReleasableValue<JavaScriptChannel> channel : javaScriptInterfaces.values()) {
-        channel.release();
-      }
-      javaScriptInterfaces.clear();
+      currentWebChromeClient = (WebChromeClientImpl) client;
+      currentWebChromeClient.setWebViewClient(currentWebViewClient);
     }
   }
 
@@ -299,16 +210,19 @@
    * Creates a host API that handles creating {@link WebView}s and invoking its methods.
    *
    * @param instanceManager maintains instances stored to communicate with Dart objects
+   * @param binaryMessenger used to communicate with Dart over asynchronous messages
    * @param webViewProxy handles creating {@link WebView}s and calling its static methods
    * @param context an Activity Context to access application assets. This value cannot be null.
    * @param containerView parent of the webView
    */
   public WebViewHostApiImpl(
       InstanceManager instanceManager,
+      BinaryMessenger binaryMessenger,
       WebViewProxy webViewProxy,
       Context context,
       @Nullable View containerView) {
     this.instanceManager = instanceManager;
+    this.binaryMessenger = binaryMessenger;
     this.webViewProxy = webViewProxy;
     this.context = context;
     this.containerView = containerView;
@@ -332,23 +246,15 @@
 
     final WebView webView =
         useHybridComposition
-            ? webViewProxy.createWebView(context)
-            : webViewProxy.createInputAwareWebView(context, containerView);
+            ? webViewProxy.createWebView(context, binaryMessenger, instanceManager)
+            : webViewProxy.createInputAwareWebView(
+                context, binaryMessenger, instanceManager, containerView);
 
     displayListenerProxy.onPostWebViewInitialization(displayManager);
     instanceManager.addDartCreatedInstance(webView, instanceId);
   }
 
   @Override
-  public void dispose(Long instanceId) {
-    final WebView instance = (WebView) instanceManager.getInstance(instanceId);
-    if (instance != null) {
-      ((Releasable) instance).release();
-      instanceManager.remove(instanceId);
-    }
-  }
-
-  @Override
   public void loadData(Long instanceId, String data, String mimeType, String encoding) {
     final WebView webView = (WebView) instanceManager.getInstance(instanceId);
     webView.loadData(data, mimeType, encoding);
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/DownloadListenerTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/DownloadListenerTest.java
index da25dac..caffbb9 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/DownloadListenerTest.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/DownloadListenerTest.java
@@ -6,11 +6,8 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 
-import android.webkit.DownloadListener;
 import io.flutter.plugins.webviewflutter.DownloadListenerHostApiImpl.DownloadListenerCreator;
 import io.flutter.plugins.webviewflutter.DownloadListenerHostApiImpl.DownloadListenerImpl;
 import org.junit.After;
@@ -67,11 +64,5 @@
             eq("mimetype"),
             eq(54L),
             any());
-
-    reset(mockFlutterApi);
-    downloadListener.release();
-    downloadListener.onDownloadStart("", "", "", "", 23);
-    verify(mockFlutterApi, never())
-        .onDownloadStart((DownloadListener) any(), any(), any(), any(), any(), eq(23), any());
   }
 }
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/JavaScriptChannelTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/JavaScriptChannelTest.java
index 4bde211..c9a5e64 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/JavaScriptChannelTest.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/JavaScriptChannelTest.java
@@ -6,8 +6,6 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 
 import android.os.Handler;
@@ -62,10 +60,5 @@
   public void postMessage() {
     javaScriptChannel.postMessage("A message post.");
     verify(mockFlutterApi).postMessage(eq(javaScriptChannel), eq("A message post."), any());
-
-    reset(mockFlutterApi);
-    javaScriptChannel.release();
-    javaScriptChannel.postMessage("a message");
-    verify(mockFlutterApi, never()).postMessage((JavaScriptChannel) any(), any(), any());
   }
 }
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebChromeClientTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebChromeClientTest.java
index 03d48d1..e821537 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebChromeClientTest.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebChromeClientTest.java
@@ -10,13 +10,11 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.net.Uri;
 import android.os.Message;
-import android.webkit.WebChromeClient;
 import android.webkit.WebResourceRequest;
 import android.webkit.WebView;
 import android.webkit.WebView.WebViewTransport;
@@ -50,21 +48,20 @@
     instanceManager = InstanceManager.open(identifier -> {});
 
     instanceManager.addDartCreatedInstance(mockWebView, 0L);
-    instanceManager.addDartCreatedInstance(mockWebViewClient, 1L);
 
     final WebChromeClientCreator webChromeClientCreator =
         new WebChromeClientCreator() {
           @Override
           public WebChromeClientImpl createWebChromeClient(
-              WebChromeClientFlutterApiImpl flutterApi, WebViewClient webViewClient) {
-            webChromeClient = super.createWebChromeClient(flutterApi, webViewClient);
+              WebChromeClientFlutterApiImpl flutterApi) {
+            webChromeClient = super.createWebChromeClient(flutterApi);
             return webChromeClient;
           }
         };
 
     hostApiImpl =
         new WebChromeClientHostApiImpl(instanceManager, webChromeClientCreator, mockFlutterApi);
-    hostApiImpl.create(2L, 1L);
+    hostApiImpl.create(2L);
   }
 
   @After
@@ -76,11 +73,6 @@
   public void onProgressChanged() {
     webChromeClient.onProgressChanged(mockWebView, 23);
     verify(mockFlutterApi).onProgressChanged(eq(webChromeClient), eq(mockWebView), eq(23L), any());
-
-    reset(mockFlutterApi);
-    webChromeClient.release();
-    webChromeClient.onProgressChanged(mockWebView, 11);
-    verify(mockFlutterApi, never()).onProgressChanged((WebChromeClient) any(), any(), any(), any());
   }
 
   @Test
@@ -91,6 +83,7 @@
     final Message message = new Message();
     message.obj = mock(WebViewTransport.class);
 
+    webChromeClient.setWebViewClient(mockWebViewClient);
     assertTrue(webChromeClient.onCreateWindow(mockWebView, message, mockOnCreateWindowWebView));
 
     /// Capture the WebViewClient used with onCreateWindow WebView.
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewClientTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewClientTest.java
index 5d0cb70..87b39d4 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewClientTest.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewClientTest.java
@@ -8,8 +8,6 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -72,11 +70,6 @@
     webViewClient.onPageStarted(mockWebView, "https://www.google.com", null);
     verify(mockFlutterApi)
         .onPageStarted(eq(webViewClient), eq(mockWebView), eq("https://www.google.com"), any());
-
-    reset(mockFlutterApi);
-    webViewClient.release();
-    webViewClient.onPageStarted(mockWebView, "", null);
-    verify(mockFlutterApi, never()).onPageStarted((WebViewClient) any(), any(), any(), any());
   }
 
   @Test
@@ -90,12 +83,6 @@
             eq("description"),
             eq("https://www.google.com"),
             any());
-
-    reset(mockFlutterApi);
-    webViewClient.release();
-    webViewClient.onReceivedError(mockWebView, 33, "", "");
-    verify(mockFlutterApi, never())
-        .onReceivedError((WebViewClient) any(), any(), any(), any(), any(), any());
   }
 
   @Test
@@ -103,11 +90,6 @@
     webViewClient.shouldOverrideUrlLoading(mockWebView, "https://www.google.com");
     verify(mockFlutterApi)
         .urlLoading(eq(webViewClient), eq(mockWebView), eq("https://www.google.com"), any());
-
-    reset(mockFlutterApi);
-    webViewClient.release();
-    webViewClient.shouldOverrideUrlLoading(mockWebView, "");
-    verify(mockFlutterApi, never()).urlLoading((WebViewClient) any(), any(), any(), any());
   }
 
   @Test
diff --git a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewTest.java b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewTest.java
index 30bc256..ecaab77 100644
--- a/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewTest.java
+++ b/packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewTest.java
@@ -15,10 +15,7 @@
 import android.webkit.ValueCallback;
 import android.webkit.WebChromeClient;
 import android.webkit.WebViewClient;
-import io.flutter.plugins.webviewflutter.DownloadListenerHostApiImpl.DownloadListenerImpl;
-import io.flutter.plugins.webviewflutter.WebChromeClientHostApiImpl.WebChromeClientImpl;
-import io.flutter.plugins.webviewflutter.WebViewClientHostApiImpl.WebViewClientImpl;
-import io.flutter.plugins.webviewflutter.WebViewHostApiImpl.InputAwareWebViewPlatformView;
+import io.flutter.plugin.common.BinaryMessenger;
 import io.flutter.plugins.webviewflutter.WebViewHostApiImpl.WebViewPlatformView;
 import java.util.HashMap;
 import org.junit.After;
@@ -39,6 +36,8 @@
 
   @Mock Context mockContext;
 
+  @Mock BinaryMessenger mockBinaryMessenger;
+
   InstanceManager testInstanceManager;
   WebViewHostApiImpl testHostApiImpl;
 
@@ -46,9 +45,11 @@
   public void setUp() {
     testInstanceManager = InstanceManager.open(identifier -> {});
 
-    when(mockWebViewProxy.createWebView(mockContext)).thenReturn(mockWebView);
+    when(mockWebViewProxy.createWebView(mockContext, mockBinaryMessenger, testInstanceManager))
+        .thenReturn(mockWebView);
     testHostApiImpl =
-        new WebViewHostApiImpl(testInstanceManager, mockWebViewProxy, mockContext, null);
+        new WebViewHostApiImpl(
+            testInstanceManager, mockBinaryMessenger, mockWebViewProxy, mockContext, null);
     testHostApiImpl.create(0L, true);
   }
 
@@ -58,112 +59,6 @@
   }
 
   @Test
-  public void releaseWebView() {
-    final WebViewPlatformView webView = new WebViewPlatformView(mockContext);
-
-    final WebViewClientImpl mockWebViewClient = mock(WebViewClientImpl.class);
-    final WebChromeClientImpl mockWebChromeClient = mock(WebChromeClientImpl.class);
-    final DownloadListenerImpl mockDownloadListener = mock(DownloadListenerImpl.class);
-    final JavaScriptChannel mockJavaScriptChannel = mock(JavaScriptChannel.class);
-
-    webView.setWebViewClient(mockWebViewClient);
-    webView.setWebChromeClient(mockWebChromeClient);
-    webView.setDownloadListener(mockDownloadListener);
-    webView.addJavascriptInterface(mockJavaScriptChannel, "jchannel");
-
-    webView.release();
-
-    verify(mockWebViewClient).release();
-    verify(mockWebChromeClient).release();
-    verify(mockDownloadListener).release();
-    verify(mockJavaScriptChannel).release();
-  }
-
-  @Test
-  public void releaseWebViewDependents() {
-    final WebViewPlatformView webView = new WebViewPlatformView(mockContext);
-
-    final WebViewClientImpl mockWebViewClient = mock(WebViewClientImpl.class);
-    final WebChromeClientImpl mockWebChromeClient = mock(WebChromeClientImpl.class);
-    final DownloadListenerImpl mockDownloadListener = mock(DownloadListenerImpl.class);
-    final JavaScriptChannel mockJavaScriptChannel = mock(JavaScriptChannel.class);
-    final JavaScriptChannel mockJavaScriptChannel2 = mock(JavaScriptChannel.class);
-
-    webView.setWebViewClient(mockWebViewClient);
-    webView.setWebChromeClient(mockWebChromeClient);
-    webView.setDownloadListener(mockDownloadListener);
-    webView.addJavascriptInterface(mockJavaScriptChannel, "jchannel");
-
-    // Release should be called on the object added above.
-    webView.addJavascriptInterface(mockJavaScriptChannel2, "jchannel");
-    verify(mockJavaScriptChannel).release();
-
-    webView.setWebViewClient(null);
-    webView.setWebChromeClient(null);
-    webView.setDownloadListener(null);
-    webView.removeJavascriptInterface("jchannel");
-
-    verify(mockWebViewClient).release();
-    verify(mockWebChromeClient).release();
-    verify(mockDownloadListener).release();
-    verify(mockJavaScriptChannel2).release();
-  }
-
-  @Test
-  public void releaseInputAwareWebView() {
-    final InputAwareWebViewPlatformView webView =
-        new InputAwareWebViewPlatformView(mockContext, null);
-
-    final WebViewClientImpl mockWebViewClient = mock(WebViewClientImpl.class);
-    final WebChromeClientImpl mockWebChromeClient = mock(WebChromeClientImpl.class);
-    final DownloadListenerImpl mockDownloadListener = mock(DownloadListenerImpl.class);
-    final JavaScriptChannel mockJavaScriptChannel = mock(JavaScriptChannel.class);
-
-    webView.setWebViewClient(mockWebViewClient);
-    webView.setWebChromeClient(mockWebChromeClient);
-    webView.setDownloadListener(mockDownloadListener);
-    webView.addJavascriptInterface(mockJavaScriptChannel, "jchannel");
-
-    webView.release();
-
-    verify(mockWebViewClient).release();
-    verify(mockWebChromeClient).release();
-    verify(mockDownloadListener).release();
-    verify(mockJavaScriptChannel).release();
-  }
-
-  @Test
-  public void releaseInputAwareWebViewDependents() {
-    final InputAwareWebViewPlatformView webView =
-        new InputAwareWebViewPlatformView(mockContext, null);
-
-    final WebViewClientImpl mockWebViewClient = mock(WebViewClientImpl.class);
-    final WebChromeClientImpl mockWebChromeClient = mock(WebChromeClientImpl.class);
-    final DownloadListenerImpl mockDownloadListener = mock(DownloadListenerImpl.class);
-    final JavaScriptChannel mockJavaScriptChannel = mock(JavaScriptChannel.class);
-    final JavaScriptChannel mockJavaScriptChannel2 = mock(JavaScriptChannel.class);
-
-    webView.setWebViewClient(mockWebViewClient);
-    webView.setWebChromeClient(mockWebChromeClient);
-    webView.setDownloadListener(mockDownloadListener);
-    webView.addJavascriptInterface(mockJavaScriptChannel, "jchannel");
-
-    // Release should be called on the object added above.
-    webView.addJavascriptInterface(mockJavaScriptChannel2, "jchannel");
-    verify(mockJavaScriptChannel).release();
-
-    webView.setWebViewClient(null);
-    webView.setWebChromeClient(null);
-    webView.setDownloadListener(null);
-    webView.removeJavascriptInterface("jchannel");
-
-    verify(mockWebViewClient).release();
-    verify(mockWebChromeClient).release();
-    verify(mockDownloadListener).release();
-    verify(mockJavaScriptChannel2).release();
-  }
-
-  @Test
   public void loadData() {
     testHostApiImpl.loadData(
         0L, "VGhpcyBkYXRhIGlzIGJhc2U2NCBlbmNvZGVkLg==", "text/plain", "base64");
diff --git a/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart
index b7c32f5..d67a9b2 100644
--- a/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart
+++ b/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart
@@ -18,6 +18,8 @@
 import 'package:flutter/services.dart';
 import 'package:flutter_test/flutter_test.dart';
 import 'package:integration_test/integration_test.dart';
+import 'package:webview_flutter_android/src/instance_manager.dart';
+import 'package:webview_flutter_android/src/weak_reference_utils.dart';
 import 'package:webview_flutter_android/webview_android.dart';
 import 'package:webview_flutter_android/webview_surface_android.dart';
 import 'package:webview_flutter_android_example/navigation_decision.dart';
@@ -103,6 +105,28 @@
     );
   });
 
+  testWidgets(
+      'withWeakRefenceTo allows encapsulating class to be garbage collected',
+      (WidgetTester tester) async {
+    final Completer<int> gcCompleter = Completer<int>();
+    final InstanceManager instanceManager = InstanceManager(
+      onWeakReferenceRemoved: gcCompleter.complete,
+    );
+
+    ClassWithCallbackClass? instance = ClassWithCallbackClass();
+    instanceManager.addHostCreatedInstance(instance.callbackClass, 0);
+    instance = null;
+
+    // Force garbage collection.
+    await IntegrationTestWidgetsFlutterBinding.instance
+        .watchPerformance(() async {
+      await tester.pumpAndSettle();
+    });
+
+    final int gcIdentifier = await gcCompleter.future;
+    expect(gcIdentifier, 0);
+  }, timeout: const Timeout(Duration(seconds: 10)));
+
   testWidgets('evaluateJavascript', (WidgetTester tester) async {
     final Completer<WebViewController> controllerCompleter =
         Completer<WebViewController>();
@@ -1512,3 +1536,33 @@
     );
   }
 }
+
+class CopyableObjectWithCallback with Copyable {
+  CopyableObjectWithCallback(this.callback);
+
+  final VoidCallback callback;
+
+  @override
+  CopyableObjectWithCallback copy() {
+    return CopyableObjectWithCallback(callback);
+  }
+}
+
+class ClassWithCallbackClass {
+  ClassWithCallbackClass() {
+    callbackClass = CopyableObjectWithCallback(
+      withWeakRefenceTo(
+        this,
+        (WeakReference<ClassWithCallbackClass> weakReference) {
+          return () {
+            // Weak reference to `this` in callback.
+            // ignore: unnecessary_statements
+            weakReference;
+          };
+        },
+      ),
+    );
+  }
+
+  late final CopyableObjectWithCallback callbackClass;
+}
diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart
index e087031..fa5b516 100644
--- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart
+++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.dart
@@ -38,7 +38,9 @@
 
   /// Global instance of [InstanceManager].
   static final InstanceManager globalInstanceManager = InstanceManager(
-    onWeakReferenceRemoved: (_) {},
+    onWeakReferenceRemoved: (int identifier) {
+      JavaObjectHostApiImpl().dispose(identifier);
+    },
   );
 
   /// Pigeon Host Api implementation for [JavaObject].
@@ -90,8 +92,6 @@
   @visibleForTesting
   static WebViewHostApiImpl api = WebViewHostApiImpl();
 
-  WebViewClient? _currentWebViewClient;
-
   /// Whether the [WebView] will be rendered with an [AndroidViewSurface].
   ///
   /// This implementation uses hybrid composition to render the WebView Widget.
@@ -330,8 +330,6 @@
   ///
   /// This will replace the current handler.
   Future<void> setWebViewClient(WebViewClient webViewClient) {
-    _currentWebViewClient = webViewClient;
-    WebViewClient.api.createFromInstance(webViewClient);
     return api.setWebViewClientFromInstance(this, webViewClient);
   }
 
@@ -375,11 +373,8 @@
   /// Registers the interface to be used when content can not be handled by the rendering engine, and should be downloaded instead.
   ///
   /// This will replace the current handler.
-  Future<void> setDownloadListener(DownloadListener? listener) async {
-    await Future.wait(<Future<void>>[
-      if (listener != null) DownloadListener.api.createFromInstance(listener),
-      api.setDownloadListenerFromInstance(this, listener)
-    ]);
+  Future<void> setDownloadListener(DownloadListener? listener) {
+    return api.setDownloadListenerFromInstance(this, listener);
   }
 
   /// Sets the chrome handler.
@@ -387,20 +382,8 @@
   /// This is an implementation of [WebChromeClient] for use in handling
   /// JavaScript dialogs, favicons, titles, and the progress. This will replace
   /// the current handler.
-  Future<void> setWebChromeClient(WebChromeClient? client) async {
-    // WebView requires a WebViewClient because of a bug fix that makes
-    // calls to WebViewClient.requestLoading/WebViewClient.urlLoading when a new
-    // window is opened. This is to make sure a url opened by `Window.open` has
-    // a secure url.
-    assert(
-      _currentWebViewClient != null,
-      "Can't set a WebChromeClient without setting a WebViewClient first.",
-    );
-    await Future.wait(<Future<void>>[
-      if (client != null)
-        WebChromeClient.api.createFromInstance(client, _currentWebViewClient!),
-      api.setWebChromeClientFromInstance(this, client),
-    ]);
+  Future<void> setWebChromeClient(WebChromeClient? client) {
+    return api.setWebChromeClientFromInstance(this, client);
   }
 
   /// Sets the background color of this WebView.
@@ -408,15 +391,6 @@
     return api.setBackgroundColorFromInstance(this, color.value);
   }
 
-  /// Releases all resources used by the [WebView].
-  ///
-  /// Any methods called after [release] will throw an exception.
-  Future<void> release() {
-    _currentWebViewClient = null;
-    WebSettings.api.disposeFromInstance(settings);
-    return api.disposeFromInstance(this);
-  }
-
   @override
   WebView copy() {
     return WebView.detached(useHybridComposition: useHybridComposition);
@@ -624,9 +598,10 @@
   /// Constructs a [JavaScriptChannel].
   JavaScriptChannel(
     this.channelName, {
-    void Function(String message)? postMessage,
+    required this.postMessage,
   }) : super.detached() {
     AndroidWebViewFlutterApis.instance.ensureSetUp();
+    api.createFromInstance(this);
   }
 
   /// Constructs a [JavaScriptChannel] without creating the associated Java
@@ -636,7 +611,7 @@
   /// create copies.
   JavaScriptChannel.detached(
     this.channelName, {
-    void Function(String message)? postMessage,
+    required this.postMessage,
   }) : super.detached();
 
   /// Pigeon Host Api implementation for [JavaScriptChannel].
@@ -647,7 +622,7 @@
   final String channelName;
 
   /// Callback method when javaScript calls `postMessage` on the object instance passed.
-  void postMessage(String message) {}
+  final void Function(String message) postMessage;
 
   @override
   JavaScriptChannel copy() {
@@ -660,25 +635,15 @@
   /// Constructs a [WebViewClient].
   WebViewClient({
     this.shouldOverrideUrlLoading = true,
-    void Function(WebView webView, String url)? onPageStarted,
-    void Function(WebView webView, String url)? onPageFinished,
-    void Function(
-      WebView webView,
-      WebResourceRequest request,
-      WebResourceError error,
-    )?
-        onReceivedRequestError,
-    void Function(
-      WebView webView,
-      int errorCode,
-      String description,
-      String failingUrl,
-    )?
-        onReceivedError,
-    void Function(WebView webView, WebResourceRequest request)? requestLoading,
-    void Function(WebView webView, String url)? urlLoading,
+    this.onPageStarted,
+    this.onPageFinished,
+    this.onReceivedRequestError,
+    @Deprecated('Only called on Android version < 23.') this.onReceivedError,
+    this.requestLoading,
+    this.urlLoading,
   }) : super.detached() {
     AndroidWebViewFlutterApis.instance.ensureSetUp();
+    api.createFromInstance(this);
   }
 
   /// Constructs a [WebViewClient] without creating the associated Java object.
@@ -687,23 +652,12 @@
   /// create copies.
   WebViewClient.detached({
     this.shouldOverrideUrlLoading = true,
-    void Function(WebView webView, String url)? onPageStarted,
-    void Function(WebView webView, String url)? onPageFinished,
-    void Function(
-      WebView webView,
-      WebResourceRequest request,
-      WebResourceError error,
-    )?
-        onReceivedRequestError,
-    void Function(
-      WebView webView,
-      int errorCode,
-      String description,
-      String failingUrl,
-    )?
-        onReceivedError,
-    void Function(WebView webView, WebResourceRequest request)? requestLoading,
-    void Function(WebView webView, String url)? urlLoading,
+    this.onPageStarted,
+    this.onPageFinished,
+    this.onReceivedRequestError,
+    @Deprecated('Only called on Android version < 23.') this.onReceivedError,
+    this.requestLoading,
+    this.urlLoading,
   }) : super.detached();
 
   /// User authentication failed on server.
@@ -812,7 +766,7 @@
   /// embedded frame changes, i.e. clicking a link whose target is an iframe, it
   /// will also not be called for fragment navigations (navigations to
   /// #fragment_id).
-  void onPageStarted(WebView webView, String url) {}
+  final void Function(WebView webView, String url)? onPageStarted;
 
   // TODO(bparrishMines): Update documentation when WebView.postVisualStateCallback is added.
   /// Notify the host application that a page has finished loading.
@@ -820,7 +774,7 @@
   /// This method is called only for main frame. Receiving an [onPageFinished]
   /// callback does not guarantee that the next frame drawn by WebView will
   /// reflect the state of the DOM at this point.
-  void onPageFinished(WebView webView, String url) {}
+  final void Function(WebView webView, String url)? onPageFinished;
 
   /// Report web resource loading error to the host application.
   ///
@@ -829,23 +783,23 @@
   /// be called for any resource (iframe, image, etc.), not just for the main
   /// page. Thus, it is recommended to perform minimum required work in this
   /// callback.
-  void onReceivedRequestError(
+  final void Function(
     WebView webView,
     WebResourceRequest request,
     WebResourceError error,
-  ) {}
+  )? onReceivedRequestError;
 
   /// Report an error to the host application.
   ///
   /// These errors are unrecoverable (i.e. the main resource is unavailable).
   /// The errorCode parameter corresponds to one of the error* constants.
   @Deprecated('Only called on Android version < 23.')
-  void onReceivedError(
+  final void Function(
     WebView webView,
     int errorCode,
     String description,
     String failingUrl,
-  ) {}
+  )? onReceivedError;
 
   // TODO(bparrishMines): Update documentation once synchronous url handling is supported.
   /// When a URL is about to be loaded in the current [WebView].
@@ -855,7 +809,8 @@
   /// [WebViewClient] is provided, setting [shouldOverrideUrlLoading] to true
   /// causes the current [WebView] to abort loading the URL, while returning
   /// false causes the [WebView] to continue loading the URL as usual.
-  void requestLoading(WebView webView, WebResourceRequest request) {}
+  final void Function(WebView webView, WebResourceRequest request)?
+      requestLoading;
 
   // TODO(bparrishMines): Update documentation once synchronous url handling is supported.
   /// When a URL is about to be loaded in the current [WebView].
@@ -865,7 +820,7 @@
   /// [WebViewClient] is provided, setting [shouldOverrideUrlLoading] to true
   /// causes the current [WebView] to abort loading the URL, while returning
   /// false causes the [WebView] to continue loading the URL as usual.
-  void urlLoading(WebView webView, String url) {}
+  final void Function(WebView webView, String url)? urlLoading;
 
   @override
   WebViewClient copy() {
@@ -885,17 +840,9 @@
 /// engine for [WebView], and should be downloaded instead.
 class DownloadListener extends JavaObject {
   /// Constructs a [DownloadListener].
-  DownloadListener({
-    void Function(
-      String url,
-      String userAgent,
-      String contentDisposition,
-      String mimetype,
-      int contentLength,
-    )?
-        onDownloadStart,
-  }) : super.detached() {
+  DownloadListener({required this.onDownloadStart}) : super.detached() {
     AndroidWebViewFlutterApis.instance.ensureSetUp();
+    api.createFromInstance(this);
   }
 
   /// Constructs a [DownloadListener] without creating the associated Java
@@ -903,43 +850,33 @@
   ///
   /// This should only be used by subclasses created by this library or to
   /// create copies.
-  DownloadListener.detached({
-    void Function(
-      String url,
-      String userAgent,
-      String contentDisposition,
-      String mimetype,
-      int contentLength,
-    )?
-        onDownloadStart,
-  }) : super.detached();
+  DownloadListener.detached({required this.onDownloadStart}) : super.detached();
 
   /// Pigeon Host Api implementation for [DownloadListener].
   @visibleForTesting
   static DownloadListenerHostApiImpl api = DownloadListenerHostApiImpl();
 
   /// Notify the host application that a file should be downloaded.
-  void onDownloadStart(
+  final void Function(
     String url,
     String userAgent,
     String contentDisposition,
     String mimetype,
     int contentLength,
-  ) {}
+  ) onDownloadStart;
 
   @override
   DownloadListener copy() {
-    return DownloadListener(onDownloadStart: onDownloadStart);
+    return DownloadListener.detached(onDownloadStart: onDownloadStart);
   }
 }
 
 /// Handles JavaScript dialogs, favicons, titles, and the progress for [WebView].
 class WebChromeClient extends JavaObject {
   /// Constructs a [WebChromeClient].
-  WebChromeClient({
-    void Function(WebView webView, int progress)? onProgressChanged,
-  }) : super.detached() {
+  WebChromeClient({this.onProgressChanged}) : super.detached() {
     AndroidWebViewFlutterApis.instance.ensureSetUp();
+    api.createFromInstance(this);
   }
 
   /// Constructs a [WebChromeClient] without creating the associated Java
@@ -947,16 +884,14 @@
   ///
   /// This should only be used by subclasses created by this library or to
   /// create copies.
-  WebChromeClient.detached({
-    void Function(WebView webView, int progress)? onProgressChanged,
-  }) : super.detached();
+  WebChromeClient.detached({this.onProgressChanged}) : super.detached();
 
   /// Pigeon Host Api implementation for [WebChromeClient].
   @visibleForTesting
   static WebChromeClientHostApiImpl api = WebChromeClientHostApiImpl();
 
   /// Notify the host application that a file should be downloaded.
-  void onProgressChanged(WebView webView, int progress) {}
+  final void Function(WebView webView, int progress)? onProgressChanged;
 
   @override
   WebChromeClient copy() {
diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart
index e793dbb..49385e5 100644
--- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart
+++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview.pigeon.dart
@@ -303,30 +303,6 @@
     }
   }
 
-  Future<void> dispose(int arg_instanceId) async {
-    final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
-        'dev.flutter.pigeon.WebViewHostApi.dispose', codec,
-        binaryMessenger: _binaryMessenger);
-    final Map<Object?, Object?>? replyMap =
-        await channel.send(<Object?>[arg_instanceId]) as Map<Object?, Object?>?;
-    if (replyMap == null) {
-      throw PlatformException(
-        code: 'channel-error',
-        message: 'Unable to establish connection on channel.',
-      );
-    } else if (replyMap['error'] != null) {
-      final Map<Object?, Object?> error =
-          (replyMap['error'] as Map<Object?, Object?>?)!;
-      throw PlatformException(
-        code: (error['code'] as String?)!,
-        message: error['message'] as String?,
-        details: error['details'],
-      );
-    } else {
-      return;
-    }
-  }
-
   Future<void> loadData(int arg_instanceId, String arg_data,
       String? arg_mimeType, String? arg_encoding) async {
     final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
@@ -1027,30 +1003,6 @@
     }
   }
 
-  Future<void> dispose(int arg_instanceId) async {
-    final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
-        'dev.flutter.pigeon.WebSettingsHostApi.dispose', codec,
-        binaryMessenger: _binaryMessenger);
-    final Map<Object?, Object?>? replyMap =
-        await channel.send(<Object?>[arg_instanceId]) as Map<Object?, Object?>?;
-    if (replyMap == null) {
-      throw PlatformException(
-        code: 'channel-error',
-        message: 'Unable to establish connection on channel.',
-      );
-    } else if (replyMap['error'] != null) {
-      final Map<Object?, Object?> error =
-          (replyMap['error'] as Map<Object?, Object?>?)!;
-      throw PlatformException(
-        code: (error['code'] as String?)!,
-        message: error['message'] as String?,
-        details: error['details'],
-      );
-    } else {
-      return;
-    }
-  }
-
   Future<void> setDomStorageEnabled(int arg_instanceId, bool arg_flag) async {
     final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
         'dev.flutter.pigeon.WebSettingsHostApi.setDomStorageEnabled', codec,
@@ -1401,31 +1353,11 @@
   static const MessageCodec<Object?> codec =
       _JavaScriptChannelFlutterApiCodec();
 
-  void dispose(int instanceId);
   void postMessage(int instanceId, String message);
   static void setup(JavaScriptChannelFlutterApi? api,
       {BinaryMessenger? binaryMessenger}) {
     {
       final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
-          'dev.flutter.pigeon.JavaScriptChannelFlutterApi.dispose', codec,
-          binaryMessenger: binaryMessenger);
-      if (api == null) {
-        channel.setMessageHandler(null);
-      } else {
-        channel.setMessageHandler((Object? message) async {
-          assert(message != null,
-              'Argument for dev.flutter.pigeon.JavaScriptChannelFlutterApi.dispose was null.');
-          final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = (args[0] as int?);
-          assert(arg_instanceId != null,
-              'Argument for dev.flutter.pigeon.JavaScriptChannelFlutterApi.dispose was null, expected non-null int.');
-          api.dispose(arg_instanceId!);
-          return;
-        });
-      }
-    }
-    {
-      final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
           'dev.flutter.pigeon.JavaScriptChannelFlutterApi.postMessage', codec,
           binaryMessenger: binaryMessenger);
       if (api == null) {
@@ -1524,7 +1456,6 @@
 abstract class WebViewClientFlutterApi {
   static const MessageCodec<Object?> codec = _WebViewClientFlutterApiCodec();
 
-  void dispose(int instanceId);
   void onPageStarted(int instanceId, int webViewInstanceId, String url);
   void onPageFinished(int instanceId, int webViewInstanceId, String url);
   void onReceivedRequestError(int instanceId, int webViewInstanceId,
@@ -1538,25 +1469,6 @@
       {BinaryMessenger? binaryMessenger}) {
     {
       final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
-          'dev.flutter.pigeon.WebViewClientFlutterApi.dispose', codec,
-          binaryMessenger: binaryMessenger);
-      if (api == null) {
-        channel.setMessageHandler(null);
-      } else {
-        channel.setMessageHandler((Object? message) async {
-          assert(message != null,
-              'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.dispose was null.');
-          final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = (args[0] as int?);
-          assert(arg_instanceId != null,
-              'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.dispose was null, expected non-null int.');
-          api.dispose(arg_instanceId!);
-          return;
-        });
-      }
-    }
-    {
-      final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
           'dev.flutter.pigeon.WebViewClientFlutterApi.onPageStarted', codec,
           binaryMessenger: binaryMessenger);
       if (api == null) {
@@ -1771,32 +1683,12 @@
 abstract class DownloadListenerFlutterApi {
   static const MessageCodec<Object?> codec = _DownloadListenerFlutterApiCodec();
 
-  void dispose(int instanceId);
   void onDownloadStart(int instanceId, String url, String userAgent,
       String contentDisposition, String mimetype, int contentLength);
   static void setup(DownloadListenerFlutterApi? api,
       {BinaryMessenger? binaryMessenger}) {
     {
       final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
-          'dev.flutter.pigeon.DownloadListenerFlutterApi.dispose', codec,
-          binaryMessenger: binaryMessenger);
-      if (api == null) {
-        channel.setMessageHandler(null);
-      } else {
-        channel.setMessageHandler((Object? message) async {
-          assert(message != null,
-              'Argument for dev.flutter.pigeon.DownloadListenerFlutterApi.dispose was null.');
-          final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = (args[0] as int?);
-          assert(arg_instanceId != null,
-              'Argument for dev.flutter.pigeon.DownloadListenerFlutterApi.dispose was null, expected non-null int.');
-          api.dispose(arg_instanceId!);
-          return;
-        });
-      }
-    }
-    {
-      final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
           'dev.flutter.pigeon.DownloadListenerFlutterApi.onDownloadStart',
           codec,
           binaryMessenger: binaryMessenger);
@@ -1849,14 +1741,12 @@
 
   static const MessageCodec<Object?> codec = _WebChromeClientHostApiCodec();
 
-  Future<void> create(
-      int arg_instanceId, int arg_webViewClientInstanceId) async {
+  Future<void> create(int arg_instanceId) async {
     final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
         'dev.flutter.pigeon.WebChromeClientHostApi.create', codec,
         binaryMessenger: _binaryMessenger);
-    final Map<Object?, Object?>? replyMap = await channel
-            .send(<Object?>[arg_instanceId, arg_webViewClientInstanceId])
-        as Map<Object?, Object?>?;
+    final Map<Object?, Object?>? replyMap =
+        await channel.send(<Object?>[arg_instanceId]) as Map<Object?, Object?>?;
     if (replyMap == null) {
       throw PlatformException(
         code: 'channel-error',
@@ -1958,31 +1848,11 @@
 abstract class WebChromeClientFlutterApi {
   static const MessageCodec<Object?> codec = _WebChromeClientFlutterApiCodec();
 
-  void dispose(int instanceId);
   void onProgressChanged(int instanceId, int webViewInstanceId, int progress);
   static void setup(WebChromeClientFlutterApi? api,
       {BinaryMessenger? binaryMessenger}) {
     {
       final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
-          'dev.flutter.pigeon.WebChromeClientFlutterApi.dispose', codec,
-          binaryMessenger: binaryMessenger);
-      if (api == null) {
-        channel.setMessageHandler(null);
-      } else {
-        channel.setMessageHandler((Object? message) async {
-          assert(message != null,
-              'Argument for dev.flutter.pigeon.WebChromeClientFlutterApi.dispose was null.');
-          final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = (args[0] as int?);
-          assert(arg_instanceId != null,
-              'Argument for dev.flutter.pigeon.WebChromeClientFlutterApi.dispose was null, expected non-null int.');
-          api.dispose(arg_instanceId!);
-          return;
-        });
-      }
-    }
-    {
-      final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
           'dev.flutter.pigeon.WebChromeClientFlutterApi.onProgressChanged',
           codec,
           binaryMessenger: binaryMessenger);
diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart
index 4f2c51c..c9fe2ef 100644
--- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart
+++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_api_impls.dart
@@ -144,15 +144,6 @@
     );
   }
 
-  /// Helper method to convert instances ids to objects.
-  Future<void> disposeFromInstance(WebView instance) async {
-    final int? instanceId = instanceManager.getIdentifier(instance);
-    if (instanceId != null) {
-      instanceManager.remove(instanceId);
-      await dispose(instanceId);
-    }
-  }
-
   /// Helper method to convert the instances ids to objects.
   Future<void> loadDataFromInstance(
     WebView instance,
@@ -368,15 +359,6 @@
   }
 
   /// Helper method to convert instances ids to objects.
-  Future<void> disposeFromInstance(WebSettings instance) async {
-    final int? instanceId = instanceManager.getIdentifier(instance);
-    if (instanceId != null) {
-      instanceManager.remove(instanceId);
-      return dispose(instanceId);
-    }
-  }
-
-  /// Helper method to convert instances ids to objects.
   Future<void> setDomStorageEnabledFromInstance(
     WebSettings instance,
     bool flag,
@@ -532,11 +514,6 @@
   final InstanceManager instanceManager;
 
   @override
-  void dispose(int instanceId) {
-    instanceManager.remove(instanceId);
-  }
-
-  @override
   void postMessage(int instanceId, String message) {
     final JavaScriptChannel? instance = instanceManager
         .getInstanceWithWeakReference(instanceId) as JavaScriptChannel?;
@@ -579,11 +556,6 @@
   final InstanceManager instanceManager;
 
   @override
-  void dispose(int instanceId) {
-    instanceManager.remove(instanceId);
-  }
-
-  @override
   void onPageFinished(int instanceId, int webViewInstanceId, String url) {
     final WebViewClient? instance = instanceManager
         .getInstanceWithWeakReference(instanceId) as WebViewClient?;
@@ -597,7 +569,9 @@
       webViewInstance != null,
       'InstanceManager does not contain an WebView with instanceId: $webViewInstanceId',
     );
-    instance!.onPageFinished(webViewInstance!, url);
+    if (instance!.onPageFinished != null) {
+      instance.onPageFinished!(webViewInstance!, url);
+    }
   }
 
   @override
@@ -614,7 +588,9 @@
       webViewInstance != null,
       'InstanceManager does not contain an WebView with instanceId: $webViewInstanceId',
     );
-    instance!.onPageStarted(webViewInstance!, url);
+    if (instance!.onPageStarted != null) {
+      instance.onPageStarted!(webViewInstance!, url);
+    }
   }
 
   @override
@@ -638,12 +614,14 @@
       'InstanceManager does not contain an WebView with instanceId: $webViewInstanceId',
     );
     // ignore: deprecated_member_use_from_same_package
-    instance!.onReceivedError(
-      webViewInstance!,
-      errorCode,
-      description,
-      failingUrl,
-    );
+    if (instance!.onReceivedError != null) {
+      instance.onReceivedError!(
+        webViewInstance!,
+        errorCode,
+        description,
+        failingUrl,
+      );
+    }
   }
 
   @override
@@ -665,11 +643,13 @@
       webViewInstance != null,
       'InstanceManager does not contain an WebView with instanceId: $webViewInstanceId',
     );
-    instance!.onReceivedRequestError(
-      webViewInstance!,
-      _toWebResourceRequest(request),
-      _toWebResourceError(error),
-    );
+    if (instance!.onReceivedRequestError != null) {
+      instance.onReceivedRequestError!(
+        webViewInstance!,
+        _toWebResourceRequest(request),
+        _toWebResourceError(error),
+      );
+    }
   }
 
   @override
@@ -690,7 +670,12 @@
       webViewInstance != null,
       'InstanceManager does not contain an WebView with instanceId: $webViewInstanceId',
     );
-    instance!.requestLoading(webViewInstance!, _toWebResourceRequest(request));
+    if (instance!.requestLoading != null) {
+      instance.requestLoading!(
+        webViewInstance!,
+        _toWebResourceRequest(request),
+      );
+    }
   }
 
   @override
@@ -711,7 +696,9 @@
       webViewInstance != null,
       'InstanceManager does not contain an WebView with instanceId: $webViewInstanceId',
     );
-    instance!.urlLoading(webViewInstance!, url);
+    if (instance!.urlLoading != null) {
+      instance.urlLoading!(webViewInstance!, url);
+    }
   }
 }
 
@@ -746,11 +733,6 @@
   final InstanceManager instanceManager;
 
   @override
-  void dispose(int instanceId) {
-    instanceManager.remove(instanceId);
-  }
-
-  @override
   void onDownloadStart(
     int instanceId,
     String url,
@@ -788,16 +770,10 @@
   final InstanceManager instanceManager;
 
   /// Helper method to convert instances ids to objects.
-  Future<void> createFromInstance(
-    WebChromeClient instance,
-    WebViewClient webViewClient,
-  ) async {
+  Future<void> createFromInstance(WebChromeClient instance) async {
     if (instanceManager.getIdentifier(instance) == null) {
       final int identifier = instanceManager.addDartCreatedInstance(instance);
-      return create(
-        identifier,
-        instanceManager.getIdentifier(webViewClient)!,
-      );
+      return create(identifier);
     }
   }
 }
@@ -812,11 +788,6 @@
   final InstanceManager instanceManager;
 
   @override
-  void dispose(int instanceId) {
-    instanceManager.remove(instanceId);
-  }
-
-  @override
   void onProgressChanged(int instanceId, int webViewInstanceId, int progress) {
     final WebChromeClient? instance = instanceManager
         .getInstanceWithWeakReference(instanceId) as WebChromeClient?;
@@ -830,7 +801,9 @@
       webViewInstance != null,
       'InstanceManager does not contain an WebView with instanceId: $webViewInstanceId',
     );
-    instance!.onProgressChanged(webViewInstance!, progress);
+    if (instance!.onProgressChanged != null) {
+      instance.onProgressChanged!(webViewInstance!, progress);
+    }
   }
 }
 
diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/weak_reference_utils.dart b/packages/webview_flutter/webview_flutter_android/lib/src/weak_reference_utils.dart
new file mode 100644
index 0000000..ad0c9eb
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_android/lib/src/weak_reference_utils.dart
@@ -0,0 +1,34 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/// Helper method for creating callbacks methods with a weak reference.
+///
+/// Example:
+/// ```
+/// final JavascriptChannelRegistry javascriptChannelRegistry = ...
+///
+/// final WKScriptMessageHandler handler = WKScriptMessageHandler(
+///   didReceiveScriptMessage: withWeakRefenceTo(
+///     javascriptChannelRegistry,
+///     (WeakReference<JavascriptChannelRegistry> weakReference) {
+///       return (
+///         WKUserContentController userContentController,
+///         WKScriptMessage message,
+///       ) {
+///         weakReference.target?.onJavascriptChannelMessage(
+///           message.name,
+///           message.body!.toString(),
+///         );
+///       };
+///     },
+///   ),
+/// );
+/// ```
+S withWeakRefenceTo<T extends Object, S extends Object>(
+  T reference,
+  S Function(WeakReference<T> weakReference) onCreate,
+) {
+  final WeakReference<T> weakReference = WeakReference<T>(reference);
+  return onCreate(weakReference);
+}
diff --git a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart
index ff6265d..4760ccf 100644
--- a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart
+++ b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart
@@ -6,6 +6,7 @@
 import 'dart:typed_data';
 
 import 'package:flutter/widgets.dart';
+import 'package:webview_flutter_android/src/weak_reference_utils.dart';
 import 'package:webview_flutter_android/webview_android_cookie_manager.dart';
 import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
 
@@ -86,12 +87,6 @@
   }
 
   @override
-  void dispose() {
-    super.dispose();
-    controller._dispose();
-  }
-
-  @override
   Widget build(BuildContext context) {
     return widget.onBuildWidget(controller);
   }
@@ -137,7 +132,10 @@
   final Map<String, WebViewAndroidJavaScriptChannel> _javaScriptChannels =
       <String, WebViewAndroidJavaScriptChannel>{};
 
-  late WebViewAndroidWebViewClient _webViewClient;
+  late android_webview.WebViewClient _webViewClient;
+
+  bool _hasNavigationDelegate = false;
+  bool _hasProgressTracking = false;
 
   /// Represents the WebView maintained by platform code.
   late final android_webview.WebView webView;
@@ -160,20 +158,50 @@
 
   /// Receives callbacks when content should be downloaded instead.
   @visibleForTesting
-  late final WebViewAndroidDownloadListener downloadListener =
-      WebViewAndroidDownloadListener(loadUrl: loadUrl);
+  late final android_webview.DownloadListener downloadListener =
+      android_webview.DownloadListener(
+    onDownloadStart: withWeakRefenceTo(
+      this,
+      (WeakReference<WebViewAndroidPlatformController> weakReference) {
+        return (
+          String url,
+          String userAgent,
+          String contentDisposition,
+          String mimetype,
+          int contentLength,
+        ) {
+          weakReference.target?._handleNavigationRequest(
+            url: url,
+            isForMainFrame: true,
+          );
+        };
+      },
+    ),
+  );
 
   /// Handles JavaScript dialogs, favicons, titles, new windows, and the progress for [android_webview.WebView].
   @visibleForTesting
-  late final WebViewAndroidWebChromeClient webChromeClient =
-      WebViewAndroidWebChromeClient();
+  late final android_webview.WebChromeClient webChromeClient =
+      android_webview.WebChromeClient(
+          onProgressChanged: withWeakRefenceTo(
+    this,
+    (WeakReference<WebViewAndroidPlatformController> weakReference) {
+      return (_, int progress) {
+        final WebViewAndroidPlatformController? controller =
+            weakReference.target;
+        if (controller != null && controller._hasProgressTracking) {
+          controller.callbacksHandler.onProgress(progress);
+        }
+      };
+    },
+  ));
 
   /// Manages the JavaScript storage APIs.
   final android_webview.WebStorage webStorage;
 
   /// Receive various notifications and requests for [android_webview.WebView].
   @visibleForTesting
-  WebViewAndroidWebViewClient get webViewClient => _webViewClient;
+  android_webview.WebViewClient get webViewClient => _webViewClient;
 
   @override
   Future<void> loadHtmlString(String html, {String? baseUrl}) {
@@ -272,10 +300,9 @@
 
   @override
   Future<void> updateSettings(WebSettings setting) async {
+    _hasProgressTracking = setting.hasProgressTracking ?? _hasProgressTracking;
     await Future.wait(<Future<void>>[
       _setUserAgent(setting.userAgent),
-      if (setting.hasProgressTracking != null)
-        _setHasProgressTracking(setting.hasProgressTracking!),
       if (setting.hasNavigationDelegate != null)
         _setHasNavigationDelegate(setting.hasNavigationDelegate!),
       if (setting.javascriptMode != null)
@@ -355,8 +382,6 @@
   @override
   Future<int> getScrollY() => webView.getScrollY();
 
-  Future<void> _dispose() => webView.release();
-
   void _setCreationParams(CreationParams creationParams) {
     final WebSettings? webSettings = creationParams.webSettings;
     if (webSettings != null) {
@@ -389,33 +414,59 @@
         .forEach(WebViewCookieManagerPlatform.instance!.setCookie);
   }
 
-  Future<void> _setHasProgressTracking(bool hasProgressTracking) async {
-    if (hasProgressTracking) {
-      webChromeClient._onProgress = callbacksHandler.onProgress;
-    } else {
-      webChromeClient._onProgress = null;
-    }
-  }
-
   Future<void> _setHasNavigationDelegate(bool hasNavigationDelegate) {
-    if (hasNavigationDelegate) {
-      downloadListener._onNavigationRequest =
-          callbacksHandler.onNavigationRequest;
-      _webViewClient = WebViewAndroidWebViewClient.handlesNavigation(
-        onPageStartedCallback: callbacksHandler.onPageStarted,
-        onPageFinishedCallback: callbacksHandler.onPageFinished,
-        onWebResourceErrorCallback: callbacksHandler.onWebResourceError,
-        loadUrl: loadUrl,
-        onNavigationRequestCallback: callbacksHandler.onNavigationRequest,
-      );
-    } else {
-      downloadListener._onNavigationRequest = null;
-      _webViewClient = WebViewAndroidWebViewClient(
-        onPageStartedCallback: callbacksHandler.onPageStarted,
-        onPageFinishedCallback: callbacksHandler.onPageFinished,
-        onWebResourceErrorCallback: callbacksHandler.onWebResourceError,
-      );
-    }
+    _hasNavigationDelegate = hasNavigationDelegate;
+
+    final WeakReference<WebViewAndroidPlatformController> weakThis =
+        WeakReference<WebViewAndroidPlatformController>(this);
+    _webViewClient = android_webview.WebViewClient(
+      shouldOverrideUrlLoading: hasNavigationDelegate,
+      onPageStarted: (_, String url) {
+        weakThis.target?.callbacksHandler.onPageStarted(url);
+      },
+      onPageFinished: (_, String url) {
+        weakThis.target?.callbacksHandler.onPageFinished(url);
+      },
+      onReceivedError: (
+        _,
+        int errorCode,
+        String description,
+        String failingUrl,
+      ) {
+        weakThis.target?.callbacksHandler.onWebResourceError(WebResourceError(
+          errorCode: errorCode,
+          description: description,
+          failingUrl: failingUrl,
+          errorType: _errorCodeToErrorType(errorCode),
+        ));
+      },
+      onReceivedRequestError: (
+        _,
+        android_webview.WebResourceRequest request,
+        android_webview.WebResourceError error,
+      ) {
+        if (request.isForMainFrame) {
+          weakThis.target?.callbacksHandler.onWebResourceError(WebResourceError(
+            errorCode: error.errorCode,
+            description: error.description,
+            failingUrl: request.url,
+            errorType: _errorCodeToErrorType(error.errorCode),
+          ));
+        }
+      },
+      urlLoading: (_, String url) {
+        weakThis.target?._handleNavigationRequest(
+          url: url,
+          isForMainFrame: true,
+        );
+      },
+      requestLoading: (_, android_webview.WebResourceRequest request) {
+        weakThis.target?._handleNavigationRequest(
+          url: request.url,
+          isForMainFrame: request.isForMainFrame,
+        );
+      },
+    );
     return webView.setWebViewClient(_webViewClient);
   }
 
@@ -444,114 +495,6 @@
   Future<void> _setZoomEnabled(bool zoomEnabled) {
     return webView.settings.setSupportZoom(zoomEnabled);
   }
-}
-
-/// Exposes a channel to receive calls from javaScript.
-class WebViewAndroidJavaScriptChannel
-    extends android_webview.JavaScriptChannel {
-  /// Creates a [WebViewAndroidJavaScriptChannel].
-  WebViewAndroidJavaScriptChannel(
-      String channelName, this.javascriptChannelRegistry)
-      : super(channelName);
-
-  /// Manages named JavaScript channels and forwarding incoming messages on the correct channel.
-  final JavascriptChannelRegistry javascriptChannelRegistry;
-
-  @override
-  void postMessage(String message) {
-    javascriptChannelRegistry.onJavascriptChannelMessage(channelName, message);
-  }
-}
-
-/// Receives callbacks when content can not be handled by the rendering engine for [WebViewAndroidPlatformController], and should be downloaded instead.
-///
-/// When handling navigation requests, this calls [onNavigationRequestCallback]
-/// when a [android_webview.WebView] attempts to navigate to a new page. If
-/// this callback return true, this calls [loadUrl].
-class WebViewAndroidDownloadListener extends android_webview.DownloadListener {
-  /// Creates a [WebViewAndroidDownloadListener].
-  WebViewAndroidDownloadListener({required this.loadUrl});
-
-  // Changed by WebViewAndroidPlatformController.
-  FutureOr<bool> Function({
-    required String url,
-    required bool isForMainFrame,
-  })? _onNavigationRequest;
-
-  /// Callback to load a URL when a navigation request is approved.
-  final Future<void> Function(String url, Map<String, String>? headers) loadUrl;
-
-  @override
-  void onDownloadStart(
-    String url,
-    String userAgent,
-    String contentDisposition,
-    String mimetype,
-    int contentLength,
-  ) {
-    if (_onNavigationRequest == null) {
-      return;
-    }
-
-    final FutureOr<bool> returnValue = _onNavigationRequest!(
-      url: url,
-      isForMainFrame: true,
-    );
-
-    if (returnValue is bool && returnValue) {
-      loadUrl(url, <String, String>{});
-    } else {
-      (returnValue as Future<bool>).then((bool shouldLoadUrl) {
-        if (shouldLoadUrl) {
-          loadUrl(url, <String, String>{});
-        }
-      });
-    }
-  }
-}
-
-/// Receives various navigation requests and errors for [WebViewAndroidPlatformController].
-///
-/// When handling navigation requests, this calls [onNavigationRequestCallback]
-/// when a [android_webview.WebView] attempts to navigate to a new page. If
-/// this callback return true, this calls [loadUrl].
-class WebViewAndroidWebViewClient extends android_webview.WebViewClient {
-  /// Creates a [WebViewAndroidWebViewClient] that doesn't handle navigation requests.
-  WebViewAndroidWebViewClient({
-    required this.onPageStartedCallback,
-    required this.onPageFinishedCallback,
-    required this.onWebResourceErrorCallback,
-  })  : loadUrl = null,
-        onNavigationRequestCallback = null,
-        super(shouldOverrideUrlLoading: false);
-
-  /// Creates a [WebViewAndroidWebViewClient] that handles navigation requests.
-  WebViewAndroidWebViewClient.handlesNavigation({
-    required this.onPageStartedCallback,
-    required this.onPageFinishedCallback,
-    required this.onWebResourceErrorCallback,
-    required this.onNavigationRequestCallback,
-    required this.loadUrl,
-  }) : super(shouldOverrideUrlLoading: true);
-
-  /// Callback when [android_webview.WebViewClient] receives a callback from [android_webview.WebViewClient].onPageStarted.
-  final void Function(String url) onPageStartedCallback;
-
-  /// Callback when [android_webview.WebViewClient] receives a callback from [android_webview.WebViewClient].onPageFinished.
-  final void Function(String url) onPageFinishedCallback;
-
-  /// Callback when [android_webview.WebViewClient] receives an error callback.
-  void Function(WebResourceError error) onWebResourceErrorCallback;
-
-  /// Checks whether a navigation request should be approved or disaproved.
-  final FutureOr<bool> Function({
-    required String url,
-    required bool isForMainFrame,
-  })? onNavigationRequestCallback;
-
-  /// Callback when a navigation request is approved.
-  final Future<void> Function(String url, Map<String, String>? headers)?
-      loadUrl;
 
   static WebResourceErrorType _errorCodeToErrorType(int errorCode) {
     switch (errorCode) {
@@ -594,110 +537,55 @@
     );
   }
 
-  /// Whether this [android_webview.WebViewClient] handles navigation requests.
-  bool get handlesNavigation =>
-      loadUrl != null && onNavigationRequestCallback != null;
-
-  @override
-  void onPageStarted(android_webview.WebView webView, String url) {
-    onPageStartedCallback(url);
-  }
-
-  @override
-  void onPageFinished(android_webview.WebView webView, String url) {
-    onPageFinishedCallback(url);
-  }
-
-  @override
-  void onReceivedError(
-    android_webview.WebView webView,
-    int errorCode,
-    String description,
-    String failingUrl,
-  ) {
-    onWebResourceErrorCallback(WebResourceError(
-      errorCode: errorCode,
-      description: description,
-      failingUrl: failingUrl,
-      errorType: _errorCodeToErrorType(errorCode),
-    ));
-  }
-
-  @override
-  void onReceivedRequestError(
-    android_webview.WebView webView,
-    android_webview.WebResourceRequest request,
-    android_webview.WebResourceError error,
-  ) {
-    if (request.isForMainFrame) {
-      onWebResourceErrorCallback(WebResourceError(
-        errorCode: error.errorCode,
-        description: error.description,
-        failingUrl: request.url,
-        errorType: _errorCodeToErrorType(error.errorCode),
-      ));
-    }
-  }
-
-  @override
-  void urlLoading(android_webview.WebView webView, String url) {
-    if (!handlesNavigation) {
+  void _handleNavigationRequest({
+    required String url,
+    required bool isForMainFrame,
+  }) {
+    if (!_hasNavigationDelegate) {
       return;
     }
 
-    final FutureOr<bool> returnValue = onNavigationRequestCallback!(
+    final FutureOr<bool> returnValue = callbacksHandler.onNavigationRequest(
       url: url,
-      isForMainFrame: true,
+      isForMainFrame: isForMainFrame,
     );
 
     if (returnValue is bool && returnValue) {
-      loadUrl!(url, <String, String>{});
+      loadUrl(url, <String, String>{});
     } else if (returnValue is Future<bool>) {
       returnValue.then((bool shouldLoadUrl) {
         if (shouldLoadUrl) {
-          loadUrl!(url, <String, String>{});
-        }
-      });
-    }
-  }
-
-  @override
-  void requestLoading(
-    android_webview.WebView webView,
-    android_webview.WebResourceRequest request,
-  ) {
-    if (!handlesNavigation) {
-      return;
-    }
-
-    final FutureOr<bool> returnValue = onNavigationRequestCallback!(
-      url: request.url,
-      isForMainFrame: request.isForMainFrame,
-    );
-
-    if (returnValue is bool && returnValue) {
-      loadUrl!(request.url, <String, String>{});
-    } else if (returnValue is Future<bool>) {
-      returnValue.then((bool shouldLoadUrl) {
-        if (shouldLoadUrl) {
-          loadUrl!(request.url, <String, String>{});
+          loadUrl(url, <String, String>{});
         }
       });
     }
   }
 }
 
-/// Handles JavaScript dialogs, favicons, titles, and the progress for [WebViewAndroidPlatformController].
-class WebViewAndroidWebChromeClient extends android_webview.WebChromeClient {
-  // Changed by WebViewAndroidPlatformController.
-  void Function(int progress)? _onProgress;
+/// Exposes a channel to receive calls from javaScript.
+class WebViewAndroidJavaScriptChannel
+    extends android_webview.JavaScriptChannel {
+  /// Creates a [WebViewAndroidJavaScriptChannel].
+  WebViewAndroidJavaScriptChannel(
+    String channelName,
+    this.javascriptChannelRegistry,
+  ) : super(
+          channelName,
+          postMessage: withWeakRefenceTo(
+            javascriptChannelRegistry,
+            (WeakReference<JavascriptChannelRegistry> weakReference) {
+              return (String message) {
+                weakReference.target?.onJavascriptChannelMessage(
+                  channelName,
+                  message,
+                );
+              };
+            },
+          ),
+        );
 
-  @override
-  void onProgressChanged(android_webview.WebView webView, int progress) {
-    if (_onProgress != null) {
-      _onProgress!(progress);
-    }
-  }
+  /// Manages named JavaScript channels and forwarding incoming messages on the correct channel.
+  final JavascriptChannelRegistry javascriptChannelRegistry;
 }
 
 /// Handles constructing [android_webview.WebView]s and calling static methods.
diff --git a/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart b/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart
index f3946ed..26f773d 100644
--- a/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart
+++ b/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart
@@ -88,8 +88,6 @@
 abstract class WebViewHostApi {
   void create(int instanceId, bool useHybridComposition);
 
-  void dispose(int instanceId);
-
   void loadData(
     int instanceId,
     String data,
@@ -169,8 +167,6 @@
 abstract class WebSettingsHostApi {
   void create(int instanceId, int webViewInstanceId);
 
-  void dispose(int instanceId);
-
   void setDomStorageEnabled(int instanceId, bool flag);
 
   void setJavaScriptCanOpenWindowsAutomatically(int instanceId, bool flag);
@@ -203,8 +199,6 @@
 
 @FlutterApi()
 abstract class JavaScriptChannelFlutterApi {
-  void dispose(int instanceId);
-
   void postMessage(int instanceId, String message);
 }
 
@@ -215,8 +209,6 @@
 
 @FlutterApi()
 abstract class WebViewClientFlutterApi {
-  void dispose(int instanceId);
-
   void onPageStarted(int instanceId, int webViewInstanceId, String url);
 
   void onPageFinished(int instanceId, int webViewInstanceId, String url);
@@ -252,8 +244,6 @@
 
 @FlutterApi()
 abstract class DownloadListenerFlutterApi {
-  void dispose(int instanceId);
-
   void onDownloadStart(
     int instanceId,
     String url,
@@ -266,7 +256,7 @@
 
 @HostApi(dartHostTestHandler: 'TestWebChromeClientHostApi')
 abstract class WebChromeClientHostApi {
-  void create(int instanceId, int webViewClientInstanceId);
+  void create(int instanceId);
 }
 
 @HostApi(dartHostTestHandler: 'TestAssetManagerHostApi')
@@ -278,8 +268,6 @@
 
 @FlutterApi()
 abstract class WebChromeClientFlutterApi {
-  void dispose(int instanceId);
-
   void onProgressChanged(int instanceId, int webViewInstanceId, int progress);
 }
 
diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml
index 62f4cc9..f5f2e3c 100644
--- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml
+++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml
@@ -2,7 +2,7 @@
 description: A Flutter plugin that provides a WebView widget on Android.
 repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_android
 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22
-version: 2.10.1
+version: 2.10.2
 
 environment:
   sdk: ">=2.14.0 <3.0.0"
diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart
index f3ec4bd..d10f4e7 100644
--- a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart
+++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.dart
@@ -270,6 +270,7 @@
         final WebViewClient mockWebViewClient = MockWebViewClient();
         when(mockWebViewClient.copy()).thenReturn(MockWebViewClient());
         when(mockWebViewClient.shouldOverrideUrlLoading).thenReturn(false);
+        instanceManager.addDartCreatedInstance(mockWebViewClient);
         webView.setWebViewClient(mockWebViewClient);
 
         final int webViewClientInstanceId =
@@ -334,6 +335,7 @@
 
         final DownloadListener mockDownloadListener = MockDownloadListener();
         when(mockDownloadListener.copy()).thenReturn(MockDownloadListener());
+        instanceManager.addDartCreatedInstance(mockDownloadListener);
         webView.setDownloadListener(mockDownloadListener);
 
         final int downloadListenerInstanceId =
@@ -345,16 +347,6 @@
       });
 
       test('setWebChromeClient', () {
-        // Setting a WebChromeClient requires setting a WebViewClient first.
-        TestWebViewClientHostApi.setup(MockTestWebViewClientHostApi());
-        WebViewClient.api = WebViewClientHostApiImpl(
-          instanceManager: instanceManager,
-        );
-        final WebViewClient mockWebViewClient = MockWebViewClient();
-        when(mockWebViewClient.copy()).thenReturn(MockWebViewClient());
-        when(mockWebViewClient.shouldOverrideUrlLoading).thenReturn(false);
-        webView.setWebViewClient(mockWebViewClient);
-
         TestWebChromeClientHostApi.setup(MockTestWebChromeClientHostApi());
         WebChromeClient.api = WebChromeClientHostApiImpl(
           instanceManager: instanceManager,
@@ -362,6 +354,7 @@
 
         final WebChromeClient mockWebChromeClient = MockWebChromeClient();
         when(mockWebChromeClient.copy()).thenReturn(MockWebChromeClient());
+        instanceManager.addDartCreatedInstance(mockWebChromeClient);
         webView.setWebChromeClient(mockWebChromeClient);
 
         final int webChromeClientInstanceId =
@@ -372,21 +365,6 @@
         ));
       });
 
-      test('release', () {
-        final MockTestWebSettingsHostApi mockWebSettingsPlatformHostApi =
-            MockTestWebSettingsHostApi();
-        TestWebSettingsHostApi.setup(mockWebSettingsPlatformHostApi);
-
-        WebSettings.api =
-            WebSettingsHostApiImpl(instanceManager: instanceManager);
-        final int webSettingsInstanceId =
-            instanceManager.getIdentifier(webView.settings)!;
-
-        webView.release();
-        verify(mockWebSettingsPlatformHostApi.dispose(webSettingsInstanceId));
-        verify(mockPlatformHostApi.dispose(webViewInstanceId));
-      });
-
       test('copy', () {
         expect(webView.copy(), isA<WebView>());
       });
@@ -544,16 +522,22 @@
       });
 
       test('postMessage', () {
+        late final String result;
+        when(mockJavaScriptChannel.postMessage).thenReturn((String message) {
+          result = message;
+        });
+
         flutterApi.postMessage(
           mockJavaScriptChannelInstanceId,
           'Hello, World!',
         );
-        verify(mockJavaScriptChannel.postMessage('Hello, World!'));
+
+        expect(result, 'Hello, World!');
       });
 
       test('copy', () {
         expect(
-          JavaScriptChannel.detached('channel').copy(),
+          JavaScriptChannel.detached('channel', postMessage: (_) {}).copy(),
           isA<JavaScriptChannel>(),
         );
       });
@@ -588,30 +572,51 @@
       });
 
       test('onPageStarted', () {
+        late final List<Object> result;
+        when(mockWebViewClient.onPageStarted).thenReturn(
+          (WebView webView, String url) {
+            result = <Object>[webView, url];
+          },
+        );
+
         flutterApi.onPageStarted(
           mockWebViewClientInstanceId,
           mockWebViewInstanceId,
           'https://www.google.com',
         );
-        verify(mockWebViewClient.onPageStarted(
-          mockWebView,
-          'https://www.google.com',
-        ));
+
+        expect(result, <Object>[mockWebView, 'https://www.google.com']);
       });
 
       test('onPageFinished', () {
+        late final List<Object> result;
+        when(mockWebViewClient.onPageFinished).thenReturn(
+          (WebView webView, String url) {
+            result = <Object>[webView, url];
+          },
+        );
+
         flutterApi.onPageFinished(
           mockWebViewClientInstanceId,
           mockWebViewInstanceId,
           'https://www.google.com',
         );
-        verify(mockWebViewClient.onPageFinished(
-          mockWebView,
-          'https://www.google.com',
-        ));
+
+        expect(result, <Object>[mockWebView, 'https://www.google.com']);
       });
 
       test('onReceivedRequestError', () {
+        late final List<Object> result;
+        when(mockWebViewClient.onReceivedRequestError).thenReturn(
+          (
+            WebView webView,
+            WebResourceRequest request,
+            WebResourceError error,
+          ) {
+            result = <Object>[webView, request, error];
+          },
+        );
+
         flutterApi.onReceivedRequestError(
           mockWebViewClientInstanceId,
           mockWebViewInstanceId,
@@ -626,14 +631,25 @@
           WebResourceErrorData(errorCode: 34, description: 'error description'),
         );
 
-        verify(mockWebViewClient.onReceivedRequestError(
-          mockWebView,
-          argThat(isNotNull),
-          argThat(isNotNull),
-        ));
+        expect(
+          result,
+          containsAllInOrder(<Object?>[mockWebView, isNotNull, isNotNull]),
+        );
       });
 
       test('onReceivedError', () {
+        late final List<Object> result;
+        when(mockWebViewClient.onReceivedError).thenReturn(
+          (
+            WebView webView,
+            int errorCode,
+            String description,
+            String failingUrl,
+          ) {
+            result = <Object>[webView, errorCode, description, failingUrl];
+          },
+        );
+
         flutterApi.onReceivedError(
           mockWebViewClientInstanceId,
           mockWebViewInstanceId,
@@ -642,15 +658,22 @@
           'https://www.google.com',
         );
 
-        verify(mockWebViewClient.onReceivedError(
-          mockWebView,
-          14,
-          'desc',
-          'https://www.google.com',
-        ));
+        expect(
+          result,
+          containsAllInOrder(
+            <Object?>[mockWebView, 14, 'desc', 'https://www.google.com'],
+          ),
+        );
       });
 
       test('requestLoading', () {
+        late final List<Object> result;
+        when(mockWebViewClient.requestLoading).thenReturn(
+          (WebView webView, WebResourceRequest request) {
+            result = <Object>[webView, request];
+          },
+        );
+
         flutterApi.requestLoading(
           mockWebViewClientInstanceId,
           mockWebViewInstanceId,
@@ -664,20 +687,27 @@
           ),
         );
 
-        verify(mockWebViewClient.requestLoading(
-          mockWebView,
-          argThat(isNotNull),
-        ));
+        expect(
+          result,
+          containsAllInOrder(<Object?>[mockWebView, isNotNull]),
+        );
       });
 
       test('urlLoading', () {
+        late final List<Object> result;
+        when(mockWebViewClient.urlLoading).thenReturn(
+          (WebView webView, String url) {
+            result = <Object>[webView, url];
+          },
+        );
+
         flutterApi.urlLoading(mockWebViewClientInstanceId,
             mockWebViewInstanceId, 'https://www.google.com');
 
-        verify(mockWebViewClient.urlLoading(
-          mockWebView,
-          'https://www.google.com',
-        ));
+        expect(
+          result,
+          containsAllInOrder(<Object?>[mockWebView, 'https://www.google.com']),
+        );
       });
 
       test('copy', () {
@@ -705,7 +735,26 @@
             instanceManager.addDartCreatedInstance(mockDownloadListener);
       });
 
-      test('onPageStarted', () {
+      test('onDownloadStart', () {
+        late final List<Object> result;
+        when(mockDownloadListener.onDownloadStart).thenReturn(
+          (
+            String url,
+            String userAgent,
+            String contentDisposition,
+            String mimetype,
+            int contentLength,
+          ) {
+            result = <Object>[
+              url,
+              userAgent,
+              contentDisposition,
+              mimetype,
+              contentLength,
+            ];
+          },
+        );
+
         flutterApi.onDownloadStart(
           mockDownloadListenerInstanceId,
           'url',
@@ -714,17 +763,26 @@
           'mimetype',
           45,
         );
-        verify(mockDownloadListener.onDownloadStart(
-          'url',
-          'userAgent',
-          'contentDescription',
-          'mimetype',
-          45,
-        ));
+
+        expect(
+          result,
+          containsAllInOrder(<Object?>[
+            'url',
+            'userAgent',
+            'contentDescription',
+            'mimetype',
+            45,
+          ]),
+        );
       });
 
       test('copy', () {
-        expect(DownloadListener.detached().copy(), isA<DownloadListener>());
+        expect(
+          DownloadListener.detached(
+            onDownloadStart: (_, __, ____, _____, ______) {},
+          ).copy(),
+          isA<DownloadListener>(),
+        );
       });
     });
 
@@ -757,13 +815,21 @@
             instanceManager.addDartCreatedInstance(mockWebView);
       });
 
-      test('onPageStarted', () {
+      test('onProgressChanged', () {
+        late final List<Object> result;
+        when(mockWebChromeClient.onProgressChanged).thenReturn(
+          (WebView webView, int progress) {
+            result = <Object>[webView, progress];
+          },
+        );
+
         flutterApi.onProgressChanged(
           mockWebChromeClientInstanceId,
           mockWebViewInstanceId,
           76,
         );
-        verify(mockWebChromeClient.onProgressChanged(mockWebView, 76));
+
+        expect(result, containsAllInOrder(<Object?>[mockWebView, 76]));
       });
 
       test('copy', () {
diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart
index 116ac83..a53b639 100644
--- a/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart
+++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_test.mocks.dart
@@ -69,12 +69,11 @@
   }
 
   @override
-  void onDownloadStart(String? url, String? userAgent,
-          String? contentDisposition, String? mimetype, int? contentLength) =>
-      super.noSuchMethod(
-          Invocation.method(#onDownloadStart,
-              [url, userAgent, contentDisposition, mimetype, contentLength]),
-          returnValueForMissingStub: null);
+  void Function(String, String, String, String, int) get onDownloadStart =>
+      (super.noSuchMethod(Invocation.getter(#onDownloadStart),
+          returnValue: (String url, String userAgent, String contentDisposition,
+              String mimetype, int contentLength) {}) as void Function(
+          String, String, String, String, int));
   @override
   _i2.DownloadListener copy() =>
       (super.noSuchMethod(Invocation.method(#copy, []),
@@ -94,9 +93,9 @@
       (super.noSuchMethod(Invocation.getter(#channelName), returnValue: '')
           as String);
   @override
-  void postMessage(String? message) =>
-      super.noSuchMethod(Invocation.method(#postMessage, [message]),
-          returnValueForMissingStub: null);
+  void Function(String) get postMessage =>
+      (super.noSuchMethod(Invocation.getter(#postMessage),
+          returnValue: (String message) {}) as void Function(String));
   @override
   _i2.JavaScriptChannel copy() =>
       (super.noSuchMethod(Invocation.method(#copy, []),
@@ -158,9 +157,8 @@
   }
 
   @override
-  void create(int? instanceId, int? webViewClientInstanceId) =>
-      super.noSuchMethod(
-          Invocation.method(#create, [instanceId, webViewClientInstanceId]),
+  void create(int? instanceId) =>
+      super.noSuchMethod(Invocation.method(#create, [instanceId]),
           returnValueForMissingStub: null);
 }
 
@@ -178,10 +176,6 @@
       Invocation.method(#create, [instanceId, webViewInstanceId]),
       returnValueForMissingStub: null);
   @override
-  void dispose(int? instanceId) =>
-      super.noSuchMethod(Invocation.method(#dispose, [instanceId]),
-          returnValueForMissingStub: null);
-  @override
   void setDomStorageEnabled(int? instanceId, bool? flag) => super.noSuchMethod(
       Invocation.method(#setDomStorageEnabled, [instanceId, flag]),
       returnValueForMissingStub: null);
@@ -290,10 +284,6 @@
           Invocation.method(#create, [instanceId, useHybridComposition]),
           returnValueForMissingStub: null);
   @override
-  void dispose(int? instanceId) =>
-      super.noSuchMethod(Invocation.method(#dispose, [instanceId]),
-          returnValueForMissingStub: null);
-  @override
   void loadData(
           int? instanceId, String? data, String? mimeType, String? encoding) =>
       super.noSuchMethod(
@@ -444,10 +434,6 @@
   }
 
   @override
-  void onProgressChanged(_i2.WebView? webView, int? progress) => super
-      .noSuchMethod(Invocation.method(#onProgressChanged, [webView, progress]),
-          returnValueForMissingStub: null);
-  @override
   _i2.WebChromeClient copy() =>
       (super.noSuchMethod(Invocation.method(#copy, []),
           returnValue: _FakeWebChromeClient_3()) as _i2.WebChromeClient);
@@ -602,11 +588,6 @@
           returnValue: Future<void>.value(),
           returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
   @override
-  _i5.Future<void> release() =>
-      (super.noSuchMethod(Invocation.method(#release, []),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
-  @override
   _i2.WebView copy() => (super.noSuchMethod(Invocation.method(#copy, []),
       returnValue: _FakeWebView_6()) as _i2.WebView);
 }
@@ -624,35 +605,6 @@
       (super.noSuchMethod(Invocation.getter(#shouldOverrideUrlLoading),
           returnValue: false) as bool);
   @override
-  void onPageStarted(_i2.WebView? webView, String? url) =>
-      super.noSuchMethod(Invocation.method(#onPageStarted, [webView, url]),
-          returnValueForMissingStub: null);
-  @override
-  void onPageFinished(_i2.WebView? webView, String? url) =>
-      super.noSuchMethod(Invocation.method(#onPageFinished, [webView, url]),
-          returnValueForMissingStub: null);
-  @override
-  void onReceivedRequestError(_i2.WebView? webView,
-          _i2.WebResourceRequest? request, _i2.WebResourceError? error) =>
-      super.noSuchMethod(
-          Invocation.method(#onReceivedRequestError, [webView, request, error]),
-          returnValueForMissingStub: null);
-  @override
-  void onReceivedError(_i2.WebView? webView, int? errorCode,
-          String? description, String? failingUrl) =>
-      super.noSuchMethod(
-          Invocation.method(
-              #onReceivedError, [webView, errorCode, description, failingUrl]),
-          returnValueForMissingStub: null);
-  @override
-  void requestLoading(_i2.WebView? webView, _i2.WebResourceRequest? request) =>
-      super.noSuchMethod(Invocation.method(#requestLoading, [webView, request]),
-          returnValueForMissingStub: null);
-  @override
-  void urlLoading(_i2.WebView? webView, String? url) =>
-      super.noSuchMethod(Invocation.method(#urlLoading, [webView, url]),
-          returnValueForMissingStub: null);
-  @override
   _i2.WebViewClient copy() => (super.noSuchMethod(Invocation.method(#copy, []),
       returnValue: _FakeWebViewClient_7()) as _i2.WebViewClient);
 }
diff --git a/packages/webview_flutter/webview_flutter_android/test/test_android_webview.pigeon.dart b/packages/webview_flutter/webview_flutter_android/test/test_android_webview.pigeon.dart
index afc80ab..91a7bf1 100644
--- a/packages/webview_flutter/webview_flutter_android/test/test_android_webview.pigeon.dart
+++ b/packages/webview_flutter/webview_flutter_android/test/test_android_webview.pigeon.dart
@@ -73,7 +73,6 @@
   static const MessageCodec<Object?> codec = _TestWebViewHostApiCodec();
 
   void create(int instanceId, bool useHybridComposition);
-  void dispose(int instanceId);
   void loadData(
       int instanceId, String data, String? mimeType, String? encoding);
   void loadDataWithBaseUrl(int instanceId, String? baseUrl, String data,
@@ -127,25 +126,6 @@
     }
     {
       final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
-          'dev.flutter.pigeon.WebViewHostApi.dispose', codec,
-          binaryMessenger: binaryMessenger);
-      if (api == null) {
-        channel.setMockMessageHandler(null);
-      } else {
-        channel.setMockMessageHandler((Object? message) async {
-          assert(message != null,
-              'Argument for dev.flutter.pigeon.WebViewHostApi.dispose was null.');
-          final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = (args[0] as int?);
-          assert(arg_instanceId != null,
-              'Argument for dev.flutter.pigeon.WebViewHostApi.dispose was null, expected non-null int.');
-          api.dispose(arg_instanceId!);
-          return <Object?, Object?>{};
-        });
-      }
-    }
-    {
-      final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
           'dev.flutter.pigeon.WebViewHostApi.loadData', codec,
           binaryMessenger: binaryMessenger);
       if (api == null) {
@@ -692,7 +672,6 @@
   static const MessageCodec<Object?> codec = _TestWebSettingsHostApiCodec();
 
   void create(int instanceId, int webViewInstanceId);
-  void dispose(int instanceId);
   void setDomStorageEnabled(int instanceId, bool flag);
   void setJavaScriptCanOpenWindowsAutomatically(int instanceId, bool flag);
   void setSupportMultipleWindows(int instanceId, bool support);
@@ -731,25 +710,6 @@
     }
     {
       final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
-          'dev.flutter.pigeon.WebSettingsHostApi.dispose', codec,
-          binaryMessenger: binaryMessenger);
-      if (api == null) {
-        channel.setMockMessageHandler(null);
-      } else {
-        channel.setMockMessageHandler((Object? message) async {
-          assert(message != null,
-              'Argument for dev.flutter.pigeon.WebSettingsHostApi.dispose was null.');
-          final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = (args[0] as int?);
-          assert(arg_instanceId != null,
-              'Argument for dev.flutter.pigeon.WebSettingsHostApi.dispose was null, expected non-null int.');
-          api.dispose(arg_instanceId!);
-          return <Object?, Object?>{};
-        });
-      }
-    }
-    {
-      final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
           'dev.flutter.pigeon.WebSettingsHostApi.setDomStorageEnabled', codec,
           binaryMessenger: binaryMessenger);
       if (api == null) {
@@ -1130,7 +1090,7 @@
 abstract class TestWebChromeClientHostApi {
   static const MessageCodec<Object?> codec = _TestWebChromeClientHostApiCodec();
 
-  void create(int instanceId, int webViewClientInstanceId);
+  void create(int instanceId);
   static void setup(TestWebChromeClientHostApi? api,
       {BinaryMessenger? binaryMessenger}) {
     {
@@ -1147,10 +1107,7 @@
           final int? arg_instanceId = (args[0] as int?);
           assert(arg_instanceId != null,
               'Argument for dev.flutter.pigeon.WebChromeClientHostApi.create was null, expected non-null int.');
-          final int? arg_webViewClientInstanceId = (args[1] as int?);
-          assert(arg_webViewClientInstanceId != null,
-              'Argument for dev.flutter.pigeon.WebChromeClientHostApi.create was null, expected non-null int.');
-          api.create(arg_instanceId!, arg_webViewClientInstanceId!);
+          api.create(arg_instanceId!);
           return <Object?, Object?>{};
         });
       }
diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart
index 73076d9..5eea6db 100644
--- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart
+++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'dart:async';
 import 'dart:typed_data';
 
 import 'package:flutter/widgets.dart';
@@ -26,10 +25,10 @@
   android_webview.WebStorage,
   android_webview.WebView,
   android_webview.WebResourceRequest,
-  WebViewAndroidDownloadListener,
+  android_webview.DownloadListener,
   WebViewAndroidJavaScriptChannel,
-  WebViewAndroidWebChromeClient,
-  WebViewAndroidWebViewClient,
+  android_webview.WebChromeClient,
+  android_webview.WebViewClient,
   JavascriptChannelRegistry,
   WebViewPlatformCallbacksHandler,
   WebViewProxy,
@@ -45,9 +44,9 @@
     late MockWebViewProxy mockWebViewProxy;
 
     late MockWebViewPlatformCallbacksHandler mockCallbacksHandler;
-    late WebViewAndroidWebViewClient webViewClient;
-    late WebViewAndroidDownloadListener downloadListener;
-    late WebViewAndroidWebChromeClient webChromeClient;
+    late android_webview.WebViewClient webViewClient;
+    late android_webview.DownloadListener downloadListener;
+    late android_webview.WebChromeClient webChromeClient;
 
     late MockJavascriptChannelRegistry mockJavascriptChannelRegistry;
 
@@ -235,7 +234,6 @@
             ),
           );
 
-          expect(testController.webViewClient.handlesNavigation, isTrue);
           expect(testController.webViewClient.shouldOverrideUrlLoading, isTrue);
         });
 
@@ -693,20 +691,20 @@
     group('WebViewPlatformCallbacksHandler', () {
       testWidgets('onPageStarted', (WidgetTester tester) async {
         await buildWidget(tester);
-        webViewClient.onPageStarted(mockWebView, 'https://google.com');
+        webViewClient.onPageStarted!(mockWebView, 'https://google.com');
         verify(mockCallbacksHandler.onPageStarted('https://google.com'));
       });
 
       testWidgets('onPageFinished', (WidgetTester tester) async {
         await buildWidget(tester);
-        webViewClient.onPageFinished(mockWebView, 'https://google.com');
+        webViewClient.onPageFinished!(mockWebView, 'https://google.com');
         verify(mockCallbacksHandler.onPageFinished('https://google.com'));
       });
 
       testWidgets('onWebResourceError from onReceivedError',
           (WidgetTester tester) async {
         await buildWidget(tester);
-        webViewClient.onReceivedError(
+        webViewClient.onReceivedError!(
           mockWebView,
           android_webview.WebViewClient.errorAuthentication,
           'description',
@@ -727,7 +725,7 @@
       testWidgets('onWebResourceError from onReceivedRequestError',
           (WidgetTester tester) async {
         await buildWidget(tester);
-        webViewClient.onReceivedRequestError(
+        webViewClient.onReceivedRequestError!(
           mockWebView,
           android_webview.WebResourceRequest(
             url: 'https://google.com',
@@ -762,7 +760,7 @@
           url: 'https://google.com',
         )).thenReturn(true);
 
-        webViewClient.urlLoading(mockWebView, 'https://google.com');
+        webViewClient.urlLoading!(mockWebView, 'https://google.com');
         verify(mockCallbacksHandler.onNavigationRequest(
           url: 'https://google.com',
           isForMainFrame: true,
@@ -778,7 +776,7 @@
           url: 'https://google.com',
         )).thenReturn(true);
 
-        webViewClient.requestLoading(
+        webViewClient.requestLoading!(
           mockWebView,
           android_webview.WebResourceRequest(
             url: 'https://google.com',
@@ -843,192 +841,4 @@
       verify(mockPlatformHostApi.setWebContentsDebuggingEnabled(false));
     });
   });
-
-  group('WebViewAndroidWebViewClient', () {
-    test(
-        'urlLoading should call loadUrl when onNavigationRequestCallback returns true',
-        () {
-      final Completer<void> completer = Completer<void>();
-      final WebViewAndroidWebViewClient webViewClient =
-          WebViewAndroidWebViewClient.handlesNavigation(
-              onPageStartedCallback: (_) {},
-              onPageFinishedCallback: (_) {},
-              onWebResourceErrorCallback: (_) {},
-              onNavigationRequestCallback: ({
-                required bool isForMainFrame,
-                required String url,
-              }) =>
-                  true,
-              loadUrl: (String url, Map<String, String>? headers) async {
-                completer.complete();
-              });
-
-      webViewClient.urlLoading(MockWebView(), 'https://flutter.dev');
-      expect(completer.isCompleted, isTrue);
-    });
-
-    test(
-        'urlLoading should call loadUrl when onNavigationRequestCallback returns a Future true',
-        () async {
-      final Completer<void> completer = Completer<void>();
-      final WebViewAndroidWebViewClient webViewClient =
-          WebViewAndroidWebViewClient.handlesNavigation(
-              onPageStartedCallback: (_) {},
-              onPageFinishedCallback: (_) {},
-              onWebResourceErrorCallback: (_) {},
-              onNavigationRequestCallback: ({
-                required bool isForMainFrame,
-                required String url,
-              }) =>
-                  Future<bool>.value(true),
-              loadUrl: (String url, Map<String, String>? headers) async {
-                completer.complete();
-              });
-
-      webViewClient.urlLoading(MockWebView(), 'https://flutter.dev');
-      expect(completer.future, completes);
-    });
-
-    test(
-        'urlLoading should not call laodUrl when onNavigationRequestCallback returns false',
-        () async {
-      final WebViewAndroidWebViewClient webViewClient =
-          WebViewAndroidWebViewClient.handlesNavigation(
-              onPageStartedCallback: (_) {},
-              onPageFinishedCallback: (_) {},
-              onWebResourceErrorCallback: (_) {},
-              onNavigationRequestCallback: ({
-                required bool isForMainFrame,
-                required String url,
-              }) =>
-                  false,
-              loadUrl: (String url, Map<String, String>? headers) async {
-                fail(
-                    'loadUrl should not be called if onNavigationRequestCallback returns false.');
-              });
-
-      webViewClient.urlLoading(MockWebView(), 'https://flutter.dev');
-    });
-
-    test(
-        'urlLoading should not call loadUrl when onNavigationRequestCallback returns a Future false',
-        () {
-      final WebViewAndroidWebViewClient webViewClient =
-          WebViewAndroidWebViewClient.handlesNavigation(
-              onPageStartedCallback: (_) {},
-              onPageFinishedCallback: (_) {},
-              onWebResourceErrorCallback: (_) {},
-              onNavigationRequestCallback: ({
-                required bool isForMainFrame,
-                required String url,
-              }) =>
-                  Future<bool>.value(false),
-              loadUrl: (String url, Map<String, String>? headers) async {
-                fail(
-                    'loadUrl should not be called if onNavigationRequestCallback returns false.');
-              });
-
-      webViewClient.urlLoading(MockWebView(), 'https://flutter.dev');
-    });
-
-    test(
-        'requestLoading should call loadUrl when onNavigationRequestCallback returns true',
-        () {
-      final Completer<void> completer = Completer<void>();
-      final MockWebResourceRequest mockRequest = MockWebResourceRequest();
-      when(mockRequest.isForMainFrame).thenReturn(true);
-      when(mockRequest.url).thenReturn('https://flutter.dev');
-      final WebViewAndroidWebViewClient webViewClient =
-          WebViewAndroidWebViewClient.handlesNavigation(
-              onPageStartedCallback: (_) {},
-              onPageFinishedCallback: (_) {},
-              onWebResourceErrorCallback: (_) {},
-              onNavigationRequestCallback: ({
-                required bool isForMainFrame,
-                required String url,
-              }) =>
-                  true,
-              loadUrl: (String url, Map<String, String>? headers) async {
-                expect(url, 'https://flutter.dev');
-                completer.complete();
-              });
-
-      webViewClient.requestLoading(MockWebView(), mockRequest);
-      expect(completer.isCompleted, isTrue);
-    });
-
-    test(
-        'requestLoading should call loadUrl when onNavigationRequestCallback returns a Future true',
-        () async {
-      final Completer<void> completer = Completer<void>();
-      final MockWebResourceRequest mockRequest = MockWebResourceRequest();
-      when(mockRequest.isForMainFrame).thenReturn(true);
-      when(mockRequest.url).thenReturn('https://flutter.dev');
-      final WebViewAndroidWebViewClient webViewClient =
-          WebViewAndroidWebViewClient.handlesNavigation(
-              onPageStartedCallback: (_) {},
-              onPageFinishedCallback: (_) {},
-              onWebResourceErrorCallback: (_) {},
-              onNavigationRequestCallback: ({
-                required bool isForMainFrame,
-                required String url,
-              }) =>
-                  Future<bool>.value(true),
-              loadUrl: (String url, Map<String, String>? headers) async {
-                expect(url, 'https://flutter.dev');
-                completer.complete();
-              });
-
-      webViewClient.requestLoading(MockWebView(), mockRequest);
-      expect(completer.future, completes);
-    });
-
-    test(
-        'requestLoading should not call loadUrl when onNavigationRequestCallback returns false',
-        () {
-      final MockWebResourceRequest mockRequest = MockWebResourceRequest();
-      when(mockRequest.isForMainFrame).thenReturn(true);
-      when(mockRequest.url).thenReturn('https://flutter.dev');
-      final WebViewAndroidWebViewClient webViewClient =
-          WebViewAndroidWebViewClient.handlesNavigation(
-              onPageStartedCallback: (_) {},
-              onPageFinishedCallback: (_) {},
-              onWebResourceErrorCallback: (_) {},
-              onNavigationRequestCallback: ({
-                required bool isForMainFrame,
-                required String url,
-              }) =>
-                  false,
-              loadUrl: (String url, Map<String, String>? headers) {
-                fail(
-                    'loadUrl should not be called if onNavigationRequestCallback returns false.');
-              });
-
-      webViewClient.requestLoading(MockWebView(), mockRequest);
-    });
-
-    test(
-        'requestLoading should not call loadUrl when onNavigationRequestCallback returns a Future false',
-        () {
-      final MockWebResourceRequest mockRequest = MockWebResourceRequest();
-      when(mockRequest.isForMainFrame).thenReturn(true);
-      when(mockRequest.url).thenReturn('https://flutter.dev');
-      final WebViewAndroidWebViewClient webViewClient =
-          WebViewAndroidWebViewClient.handlesNavigation(
-              onPageStartedCallback: (_) {},
-              onPageFinishedCallback: (_) {},
-              onWebResourceErrorCallback: (_) {},
-              onNavigationRequestCallback: ({
-                required bool isForMainFrame,
-                required String url,
-              }) =>
-                  Future<bool>.value(false),
-              loadUrl: (String url, Map<String, String>? headers) {
-                fail(
-                    'loadUrl should not be called if onNavigationRequestCallback returns false.');
-              });
-
-      webViewClient.requestLoading(MockWebView(), mockRequest);
-    });
-  });
 }
diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart
index acaff1c..860562e 100644
--- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart
+++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart
@@ -305,11 +305,6 @@
           returnValue: Future<void>.value(),
           returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
   @override
-  _i5.Future<void> release() =>
-      (super.noSuchMethod(Invocation.method(#release, []),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
-  @override
   _i2.WebView copy() => (super.noSuchMethod(Invocation.method(#copy, []),
       returnValue: _FakeWebView_3()) as _i2.WebView);
 }
@@ -344,28 +339,20 @@
           returnValue: <String, String>{}) as Map<String, String>);
 }
 
-/// A class which mocks [WebViewAndroidDownloadListener].
+/// A class which mocks [DownloadListener].
 ///
 /// See the documentation for Mockito's code generation for more information.
-class MockWebViewAndroidDownloadListener extends _i1.Mock
-    implements _i7.WebViewAndroidDownloadListener {
-  MockWebViewAndroidDownloadListener() {
+class MockDownloadListener extends _i1.Mock implements _i2.DownloadListener {
+  MockDownloadListener() {
     _i1.throwOnMissingStub(this);
   }
 
   @override
-  _i5.Future<void> Function(String, Map<String, String>?) get loadUrl =>
-      (super.noSuchMethod(Invocation.getter(#loadUrl),
-          returnValue: (String url, Map<String, String>? headers) =>
-              Future<void>.value()) as _i5.Future<void> Function(
-          String, Map<String, String>?));
-  @override
-  void onDownloadStart(String? url, String? userAgent,
-          String? contentDisposition, String? mimetype, int? contentLength) =>
-      super.noSuchMethod(
-          Invocation.method(#onDownloadStart,
-              [url, userAgent, contentDisposition, mimetype, contentLength]),
-          returnValueForMissingStub: null);
+  void Function(String, String, String, String, int) get onDownloadStart =>
+      (super.noSuchMethod(Invocation.getter(#onDownloadStart),
+          returnValue: (String url, String userAgent, String contentDisposition,
+              String mimetype, int contentLength) {}) as void Function(
+          String, String, String, String, int));
   @override
   _i2.DownloadListener copy() =>
       (super.noSuchMethod(Invocation.method(#copy, []),
@@ -391,101 +378,42 @@
       (super.noSuchMethod(Invocation.getter(#channelName), returnValue: '')
           as String);
   @override
-  void postMessage(String? message) =>
-      super.noSuchMethod(Invocation.method(#postMessage, [message]),
-          returnValueForMissingStub: null);
+  void Function(String) get postMessage =>
+      (super.noSuchMethod(Invocation.getter(#postMessage),
+          returnValue: (String message) {}) as void Function(String));
   @override
   _i2.JavaScriptChannel copy() =>
       (super.noSuchMethod(Invocation.method(#copy, []),
           returnValue: _FakeJavaScriptChannel_6()) as _i2.JavaScriptChannel);
 }
 
-/// A class which mocks [WebViewAndroidWebChromeClient].
+/// A class which mocks [WebChromeClient].
 ///
 /// See the documentation for Mockito's code generation for more information.
-class MockWebViewAndroidWebChromeClient extends _i1.Mock
-    implements _i7.WebViewAndroidWebChromeClient {
-  MockWebViewAndroidWebChromeClient() {
+class MockWebChromeClient extends _i1.Mock implements _i2.WebChromeClient {
+  MockWebChromeClient() {
     _i1.throwOnMissingStub(this);
   }
 
   @override
-  void onProgressChanged(_i2.WebView? webView, int? progress) => super
-      .noSuchMethod(Invocation.method(#onProgressChanged, [webView, progress]),
-          returnValueForMissingStub: null);
-  @override
   _i2.WebChromeClient copy() =>
       (super.noSuchMethod(Invocation.method(#copy, []),
           returnValue: _FakeWebChromeClient_7()) as _i2.WebChromeClient);
 }
 
-/// A class which mocks [WebViewAndroidWebViewClient].
+/// A class which mocks [WebViewClient].
 ///
 /// See the documentation for Mockito's code generation for more information.
-class MockWebViewAndroidWebViewClient extends _i1.Mock
-    implements _i7.WebViewAndroidWebViewClient {
-  MockWebViewAndroidWebViewClient() {
+class MockWebViewClient extends _i1.Mock implements _i2.WebViewClient {
+  MockWebViewClient() {
     _i1.throwOnMissingStub(this);
   }
 
   @override
-  void Function(String) get onPageStartedCallback =>
-      (super.noSuchMethod(Invocation.getter(#onPageStartedCallback),
-          returnValue: (String url) {}) as void Function(String));
-  @override
-  void Function(String) get onPageFinishedCallback =>
-      (super.noSuchMethod(Invocation.getter(#onPageFinishedCallback),
-          returnValue: (String url) {}) as void Function(String));
-  @override
-  void Function(_i4.WebResourceError) get onWebResourceErrorCallback =>
-      (super.noSuchMethod(Invocation.getter(#onWebResourceErrorCallback),
-              returnValue: (_i4.WebResourceError error) {})
-          as void Function(_i4.WebResourceError));
-  @override
-  set onWebResourceErrorCallback(
-          void Function(_i4.WebResourceError)? _onWebResourceErrorCallback) =>
-      super.noSuchMethod(
-          Invocation.setter(
-              #onWebResourceErrorCallback, _onWebResourceErrorCallback),
-          returnValueForMissingStub: null);
-  @override
-  bool get handlesNavigation =>
-      (super.noSuchMethod(Invocation.getter(#handlesNavigation),
-          returnValue: false) as bool);
-  @override
   bool get shouldOverrideUrlLoading =>
       (super.noSuchMethod(Invocation.getter(#shouldOverrideUrlLoading),
           returnValue: false) as bool);
   @override
-  void onPageStarted(_i2.WebView? webView, String? url) =>
-      super.noSuchMethod(Invocation.method(#onPageStarted, [webView, url]),
-          returnValueForMissingStub: null);
-  @override
-  void onPageFinished(_i2.WebView? webView, String? url) =>
-      super.noSuchMethod(Invocation.method(#onPageFinished, [webView, url]),
-          returnValueForMissingStub: null);
-  @override
-  void onReceivedError(_i2.WebView? webView, int? errorCode,
-          String? description, String? failingUrl) =>
-      super.noSuchMethod(
-          Invocation.method(
-              #onReceivedError, [webView, errorCode, description, failingUrl]),
-          returnValueForMissingStub: null);
-  @override
-  void onReceivedRequestError(_i2.WebView? webView,
-          _i2.WebResourceRequest? request, _i2.WebResourceError? error) =>
-      super.noSuchMethod(
-          Invocation.method(#onReceivedRequestError, [webView, request, error]),
-          returnValueForMissingStub: null);
-  @override
-  void urlLoading(_i2.WebView? webView, String? url) =>
-      super.noSuchMethod(Invocation.method(#urlLoading, [webView, url]),
-          returnValueForMissingStub: null);
-  @override
-  void requestLoading(_i2.WebView? webView, _i2.WebResourceRequest? request) =>
-      super.noSuchMethod(Invocation.method(#requestLoading, [webView, request]),
-          returnValueForMissingStub: null);
-  @override
   _i2.WebViewClient copy() => (super.noSuchMethod(Invocation.method(#copy, []),
       returnValue: _FakeWebViewClient_8()) as _i2.WebViewClient);
 }