[webview_flutter_android] Copies Android implementation of webview_flutter from v4_webview (#6851)

* new android release

* Version bump

* update all plugins config

* fix lints and add action item to changelog
diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md
index fbb502c..a7a820d 100644
--- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md
+++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md
@@ -1,3 +1,9 @@
+## 3.0.0
+
+* **BREAKING CHANGE** Updates platform implementation to `2.0.0` release of
+  `webview_flutter_platform_interface`. See
+  [webview_flutter](https://pub.dev/packages/webview_flutter/versions/4.0.0) for updated usage.
+
 ## 2.10.4
 
 * Updates code for `no_leading_underscores_for_local_identifiers` lint.
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..15c80cc 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
@@ -1,7 +1,7 @@
 // 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.
-// Autogenerated from Pigeon (v4.0.2), do not edit directly.
+// Autogenerated from Pigeon (v4.2.3), do not edit directly.
 // See also: https://pub.dev/packages/pigeon
 
 package io.flutter.plugins.webviewflutter;
@@ -17,6 +17,7 @@
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -342,22 +343,22 @@
 
     void error(Throwable error);
   }
-
-  private static class JavaObjectHostApiCodec extends StandardMessageCodec {
-    public static final JavaObjectHostApiCodec INSTANCE = new JavaObjectHostApiCodec();
-
-    private JavaObjectHostApiCodec() {}
-  }
-
-  /** Generated interface from Pigeon that represents a handler of messages from Flutter. */
+  /**
+   * Handles methods calls to the native Java Object class.
+   *
+   * <p>Also handles calls to remove the reference to an instance with `dispose`.
+   *
+   * <p>See https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html.
+   *
+   * <p>Generated interface from Pigeon that represents a handler of messages from Flutter.
+   */
   public interface JavaObjectHostApi {
     void dispose(@NonNull Long identifier);
 
     /** The codec used by JavaObjectHostApi. */
     static MessageCodec<Object> getCodec() {
-      return JavaObjectHostApiCodec.INSTANCE;
+      return new StandardMessageCodec();
     }
-
     /**
      * Sets up an instance of `JavaObjectHostApi` to handle messages through the `binaryMessenger`.
      */
@@ -372,6 +373,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number identifierArg = (Number) args.get(0);
                   if (identifierArg == null) {
                     throw new NullPointerException("identifierArg unexpectedly null.");
@@ -389,14 +391,13 @@
       }
     }
   }
-
-  private static class JavaObjectFlutterApiCodec extends StandardMessageCodec {
-    public static final JavaObjectFlutterApiCodec INSTANCE = new JavaObjectFlutterApiCodec();
-
-    private JavaObjectFlutterApiCodec() {}
-  }
-
-  /** Generated class from Pigeon that represents Flutter messages that can be called from Java. */
+  /**
+   * Handles callbacks methods for the native Java Object class.
+   *
+   * <p>See https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html.
+   *
+   * <p>Generated class from Pigeon that represents Flutter messages that can be called from Java.
+   */
   public static class JavaObjectFlutterApi {
     private final BinaryMessenger binaryMessenger;
 
@@ -407,9 +408,9 @@
     public interface Reply<T> {
       void reply(T reply);
     }
-
+    /** The codec used by JavaObjectFlutterApi. */
     static MessageCodec<Object> getCodec() {
-      return JavaObjectFlutterApiCodec.INSTANCE;
+      return new StandardMessageCodec();
     }
 
     public void dispose(@NonNull Long identifierArg, Reply<Void> callback) {
@@ -417,19 +418,12 @@
           new BasicMessageChannel<>(
               binaryMessenger, "dev.flutter.pigeon.JavaObjectFlutterApi.dispose", getCodec());
       channel.send(
-          new ArrayList<Object>(Arrays.asList(identifierArg)),
+          new ArrayList<Object>(Collections.singletonList(identifierArg)),
           channelReply -> {
             callback.reply(null);
           });
     }
   }
-
-  private static class CookieManagerHostApiCodec extends StandardMessageCodec {
-    public static final CookieManagerHostApiCodec INSTANCE = new CookieManagerHostApiCodec();
-
-    private CookieManagerHostApiCodec() {}
-  }
-
   /** Generated interface from Pigeon that represents a handler of messages from Flutter. */
   public interface CookieManagerHostApi {
     void clearCookies(Result<Boolean> result);
@@ -438,9 +432,8 @@
 
     /** The codec used by CookieManagerHostApi. */
     static MessageCodec<Object> getCodec() {
-      return CookieManagerHostApiCodec.INSTANCE;
+      return new StandardMessageCodec();
     }
-
     /**
      * Sets up an instance of `CookieManagerHostApi` to handle messages through the
      * `binaryMessenger`.
@@ -490,6 +483,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   String urlArg = (String) args.get(0);
                   if (urlArg == null) {
                     throw new NullPointerException("urlArg unexpectedly null.");
@@ -518,7 +512,7 @@
     private WebViewHostApiCodec() {}
 
     @Override
-    protected Object readValueOfType(byte type, ByteBuffer buffer) {
+    protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) {
       switch (type) {
         case (byte) 128:
           return WebViewPoint.fromMap((Map<String, Object>) readValue(buffer));
@@ -529,7 +523,7 @@
     }
 
     @Override
-    protected void writeValue(ByteArrayOutputStream stream, Object value) {
+    protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) {
       if (value instanceof WebViewPoint) {
         stream.write(128);
         writeValue(stream, ((WebViewPoint) value).toMap());
@@ -543,8 +537,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,
@@ -619,7 +611,6 @@
     static MessageCodec<Object> getCodec() {
       return WebViewHostApiCodec.INSTANCE;
     }
-
     /** Sets up an instance of `WebViewHostApi` to handle messages through the `binaryMessenger`. */
     static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) {
       {
@@ -632,6 +623,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -656,31 +648,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(
@@ -688,6 +655,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -725,6 +693,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -764,6 +733,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -800,6 +770,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -834,6 +805,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -860,6 +832,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -886,6 +859,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -912,6 +886,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -937,6 +912,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -962,6 +938,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -987,6 +964,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -1020,6 +998,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -1064,6 +1043,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -1090,6 +1070,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -1126,6 +1107,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -1162,6 +1144,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -1188,6 +1171,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -1214,6 +1198,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -1243,6 +1228,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Boolean enabledArg = (Boolean) args.get(0);
                   if (enabledArg == null) {
                     throw new NullPointerException("enabledArg unexpectedly null.");
@@ -1268,6 +1254,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -1303,6 +1290,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -1339,6 +1327,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -1375,6 +1364,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -1405,6 +1395,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -1435,6 +1426,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -1458,19 +1450,10 @@
       }
     }
   }
-
-  private static class WebSettingsHostApiCodec extends StandardMessageCodec {
-    public static final WebSettingsHostApiCodec INSTANCE = new WebSettingsHostApiCodec();
-
-    private WebSettingsHostApiCodec() {}
-  }
-
   /** Generated interface from Pigeon that represents a handler of messages from Flutter. */
   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);
@@ -1497,9 +1480,8 @@
 
     /** The codec used by WebSettingsHostApi. */
     static MessageCodec<Object> getCodec() {
-      return WebSettingsHostApiCodec.INSTANCE;
+      return new StandardMessageCodec();
     }
-
     /**
      * Sets up an instance of `WebSettingsHostApi` to handle messages through the `binaryMessenger`.
      */
@@ -1514,6 +1496,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -1538,31 +1521,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());
@@ -1572,6 +1530,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -1604,6 +1563,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -1636,6 +1596,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -1668,6 +1629,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -1700,6 +1662,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -1730,6 +1693,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -1762,6 +1726,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -1794,6 +1759,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -1826,6 +1792,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -1858,6 +1825,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -1890,6 +1858,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -1922,6 +1891,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -1944,23 +1914,14 @@
       }
     }
   }
-
-  private static class JavaScriptChannelHostApiCodec extends StandardMessageCodec {
-    public static final JavaScriptChannelHostApiCodec INSTANCE =
-        new JavaScriptChannelHostApiCodec();
-
-    private JavaScriptChannelHostApiCodec() {}
-  }
-
   /** Generated interface from Pigeon that represents a handler of messages from Flutter. */
   public interface JavaScriptChannelHostApi {
     void create(@NonNull Long instanceId, @NonNull String channelName);
 
     /** The codec used by JavaScriptChannelHostApi. */
     static MessageCodec<Object> getCodec() {
-      return JavaScriptChannelHostApiCodec.INSTANCE;
+      return new StandardMessageCodec();
     }
-
     /**
      * Sets up an instance of `JavaScriptChannelHostApi` to handle messages through the
      * `binaryMessenger`.
@@ -1976,6 +1937,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -1998,14 +1960,6 @@
       }
     }
   }
-
-  private static class JavaScriptChannelFlutterApiCodec extends StandardMessageCodec {
-    public static final JavaScriptChannelFlutterApiCodec INSTANCE =
-        new JavaScriptChannelFlutterApiCodec();
-
-    private JavaScriptChannelFlutterApiCodec() {}
-  }
-
   /** Generated class from Pigeon that represents Flutter messages that can be called from Java. */
   public static class JavaScriptChannelFlutterApi {
     private final BinaryMessenger binaryMessenger;
@@ -2017,22 +1971,9 @@
     public interface Reply<T> {
       void reply(T reply);
     }
-
+    /** The codec used by JavaScriptChannelFlutterApi. */
     static MessageCodec<Object> getCodec() {
-      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);
-          });
+      return new StandardMessageCodec();
     }
 
     public void postMessage(
@@ -2049,22 +1990,17 @@
           });
     }
   }
-
-  private static class WebViewClientHostApiCodec extends StandardMessageCodec {
-    public static final WebViewClientHostApiCodec INSTANCE = new WebViewClientHostApiCodec();
-
-    private WebViewClientHostApiCodec() {}
-  }
-
   /** Generated interface from Pigeon that represents a handler of messages from Flutter. */
   public interface WebViewClientHostApi {
-    void create(@NonNull Long instanceId, @NonNull Boolean shouldOverrideUrlLoading);
+    void create(@NonNull Long instanceId);
+
+    void setSynchronousReturnValueForShouldOverrideUrlLoading(
+        @NonNull Long instanceId, @NonNull Boolean value);
 
     /** The codec used by WebViewClientHostApi. */
     static MessageCodec<Object> getCodec() {
-      return WebViewClientHostApiCodec.INSTANCE;
+      return new StandardMessageCodec();
     }
-
     /**
      * Sets up an instance of `WebViewClientHostApi` to handle messages through the
      * `binaryMessenger`.
@@ -2080,18 +2016,45 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
                   }
-                  Boolean shouldOverrideUrlLoadingArg = (Boolean) args.get(1);
-                  if (shouldOverrideUrlLoadingArg == null) {
-                    throw new NullPointerException(
-                        "shouldOverrideUrlLoadingArg unexpectedly null.");
+                  api.create((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.WebViewClientHostApi.setSynchronousReturnValueForShouldOverrideUrlLoading",
+                getCodec());
+        if (api != null) {
+          channel.setMessageHandler(
+              (message, reply) -> {
+                Map<String, Object> wrapped = new HashMap<>();
+                try {
+                  ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
+                  Number instanceIdArg = (Number) args.get(0);
+                  if (instanceIdArg == null) {
+                    throw new NullPointerException("instanceIdArg unexpectedly null.");
                   }
-                  api.create(
-                      (instanceIdArg == null) ? null : instanceIdArg.longValue(),
-                      shouldOverrideUrlLoadingArg);
+                  Boolean valueArg = (Boolean) args.get(1);
+                  if (valueArg == null) {
+                    throw new NullPointerException("valueArg unexpectedly null.");
+                  }
+                  api.setSynchronousReturnValueForShouldOverrideUrlLoading(
+                      (instanceIdArg == null) ? null : instanceIdArg.longValue(), valueArg);
                   wrapped.put("result", null);
                 } catch (Error | RuntimeException exception) {
                   wrapped.put("error", wrapError(exception));
@@ -2111,7 +2074,7 @@
     private WebViewClientFlutterApiCodec() {}
 
     @Override
-    protected Object readValueOfType(byte type, ByteBuffer buffer) {
+    protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) {
       switch (type) {
         case (byte) 128:
           return WebResourceErrorData.fromMap((Map<String, Object>) readValue(buffer));
@@ -2125,7 +2088,7 @@
     }
 
     @Override
-    protected void writeValue(ByteArrayOutputStream stream, Object value) {
+    protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) {
       if (value instanceof WebResourceErrorData) {
         stream.write(128);
         writeValue(stream, ((WebResourceErrorData) value).toMap());
@@ -2149,22 +2112,11 @@
     public interface Reply<T> {
       void reply(T reply);
     }
-
+    /** The codec used by WebViewClientFlutterApi. */
     static MessageCodec<Object> getCodec() {
       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,
@@ -2275,22 +2227,14 @@
           });
     }
   }
-
-  private static class DownloadListenerHostApiCodec extends StandardMessageCodec {
-    public static final DownloadListenerHostApiCodec INSTANCE = new DownloadListenerHostApiCodec();
-
-    private DownloadListenerHostApiCodec() {}
-  }
-
   /** Generated interface from Pigeon that represents a handler of messages from Flutter. */
   public interface DownloadListenerHostApi {
     void create(@NonNull Long instanceId);
 
     /** The codec used by DownloadListenerHostApi. */
     static MessageCodec<Object> getCodec() {
-      return DownloadListenerHostApiCodec.INSTANCE;
+      return new StandardMessageCodec();
     }
-
     /**
      * Sets up an instance of `DownloadListenerHostApi` to handle messages through the
      * `binaryMessenger`.
@@ -2306,6 +2250,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -2323,14 +2268,6 @@
       }
     }
   }
-
-  private static class DownloadListenerFlutterApiCodec extends StandardMessageCodec {
-    public static final DownloadListenerFlutterApiCodec INSTANCE =
-        new DownloadListenerFlutterApiCodec();
-
-    private DownloadListenerFlutterApiCodec() {}
-  }
-
   /** Generated class from Pigeon that represents Flutter messages that can be called from Java. */
   public static class DownloadListenerFlutterApi {
     private final BinaryMessenger binaryMessenger;
@@ -2342,20 +2279,9 @@
     public interface Reply<T> {
       void reply(T reply);
     }
-
+    /** The codec used by DownloadListenerFlutterApi. */
     static MessageCodec<Object> getCodec() {
-      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);
-          });
+      return new StandardMessageCodec();
     }
 
     public void onDownloadStart(
@@ -2385,22 +2311,14 @@
           });
     }
   }
-
-  private static class WebChromeClientHostApiCodec extends StandardMessageCodec {
-    public static final WebChromeClientHostApiCodec INSTANCE = new WebChromeClientHostApiCodec();
-
-    private WebChromeClientHostApiCodec() {}
-  }
-
   /** 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() {
-      return WebChromeClientHostApiCodec.INSTANCE;
+      return new StandardMessageCodec();
     }
-
     /**
      * Sets up an instance of `WebChromeClientHostApi` to handle messages through the
      * `binaryMessenger`.
@@ -2416,19 +2334,12 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   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));
@@ -2441,14 +2352,6 @@
       }
     }
   }
-
-  private static class FlutterAssetManagerHostApiCodec extends StandardMessageCodec {
-    public static final FlutterAssetManagerHostApiCodec INSTANCE =
-        new FlutterAssetManagerHostApiCodec();
-
-    private FlutterAssetManagerHostApiCodec() {}
-  }
-
   /** Generated interface from Pigeon that represents a handler of messages from Flutter. */
   public interface FlutterAssetManagerHostApi {
     @NonNull
@@ -2459,9 +2362,8 @@
 
     /** The codec used by FlutterAssetManagerHostApi. */
     static MessageCodec<Object> getCodec() {
-      return FlutterAssetManagerHostApiCodec.INSTANCE;
+      return new StandardMessageCodec();
     }
-
     /**
      * Sets up an instance of `FlutterAssetManagerHostApi` to handle messages through the
      * `binaryMessenger`.
@@ -2477,6 +2379,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   String pathArg = (String) args.get(0);
                   if (pathArg == null) {
                     throw new NullPointerException("pathArg unexpectedly null.");
@@ -2504,6 +2407,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   String nameArg = (String) args.get(0);
                   if (nameArg == null) {
                     throw new NullPointerException("nameArg unexpectedly null.");
@@ -2521,14 +2425,6 @@
       }
     }
   }
-
-  private static class WebChromeClientFlutterApiCodec extends StandardMessageCodec {
-    public static final WebChromeClientFlutterApiCodec INSTANCE =
-        new WebChromeClientFlutterApiCodec();
-
-    private WebChromeClientFlutterApiCodec() {}
-  }
-
   /** Generated class from Pigeon that represents Flutter messages that can be called from Java. */
   public static class WebChromeClientFlutterApi {
     private final BinaryMessenger binaryMessenger;
@@ -2540,20 +2436,9 @@
     public interface Reply<T> {
       void reply(T reply);
     }
-
+    /** The codec used by WebChromeClientFlutterApi. */
     static MessageCodec<Object> getCodec() {
-      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);
-          });
+      return new StandardMessageCodec();
     }
 
     public void onProgressChanged(
@@ -2573,13 +2458,6 @@
           });
     }
   }
-
-  private static class WebStorageHostApiCodec extends StandardMessageCodec {
-    public static final WebStorageHostApiCodec INSTANCE = new WebStorageHostApiCodec();
-
-    private WebStorageHostApiCodec() {}
-  }
-
   /** Generated interface from Pigeon that represents a handler of messages from Flutter. */
   public interface WebStorageHostApi {
     void create(@NonNull Long instanceId);
@@ -2588,9 +2466,8 @@
 
     /** The codec used by WebStorageHostApi. */
     static MessageCodec<Object> getCodec() {
-      return WebStorageHostApiCodec.INSTANCE;
+      return new StandardMessageCodec();
     }
-
     /**
      * Sets up an instance of `WebStorageHostApi` to handle messages through the `binaryMessenger`.
      */
@@ -2605,6 +2482,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -2630,6 +2508,7 @@
                 Map<String, Object> wrapped = new HashMap<>();
                 try {
                   ArrayList<Object> args = (ArrayList<Object>) message;
+                  assert args != null;
                   Number instanceIdArg = (Number) args.get(0);
                   if (instanceIdArg == null) {
                     throw new NullPointerException("instanceIdArg unexpectedly null.");
@@ -2648,7 +2527,8 @@
     }
   }
 
-  private static Map<String, Object> wrapError(Throwable exception) {
+  @NonNull
+  private static Map<String, Object> wrapError(@NonNull Throwable exception) {
     Map<String, Object> errorMap = new HashMap<>();
     errorMap.put("message", exception.toString());
     errorMap.put("code", exception.getClass().getSimpleName());
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..09a34f2 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,10 +14,10 @@
 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;
+import java.util.Objects;
 
 /**
  * Host api implementation for {@link WebViewClient}.
@@ -29,73 +29,53 @@
   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;
-    private final boolean shouldOverrideUrlLoading;
+  public static class WebViewClientImpl extends WebViewClient {
+    private final WebViewClientFlutterApiImpl flutterApi;
+    private boolean returnValueForShouldOverrideUrlLoading = false;
 
     /**
      * Creates a {@link WebViewClient} that passes arguments of callbacks methods to Dart.
      *
      * @param flutterApi handles sending messages to Dart
-     * @param shouldOverrideUrlLoading whether loading a url should be overridden
      */
-    public WebViewClientImpl(
-        @NonNull WebViewClientFlutterApiImpl flutterApi, boolean shouldOverrideUrlLoading) {
-      this.shouldOverrideUrlLoading = shouldOverrideUrlLoading;
+    public WebViewClientImpl(@NonNull WebViewClientFlutterApiImpl flutterApi) {
       this.flutterApi = flutterApi;
     }
 
     @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 -> {});
-      }
-      return shouldOverrideUrlLoading;
+      flutterApi.requestLoading(this, view, request, reply -> {});
+      return returnValueForShouldOverrideUrlLoading;
     }
 
     @Override
     public boolean shouldOverrideUrlLoading(WebView view, String url) {
-      if (flutterApi != null) {
-        flutterApi.urlLoading(this, view, url, reply -> {});
-      }
-      return shouldOverrideUrlLoading;
+      flutterApi.urlLoading(this, view, url, reply -> {});
+      return returnValueForShouldOverrideUrlLoading;
     }
 
     @Override
@@ -105,11 +85,9 @@
       // truly lost.
     }
 
-    public void release() {
-      if (flutterApi != null) {
-        flutterApi.dispose(this, reply -> {});
-      }
-      flutterApi = null;
+    /** Sets return value for {@link #shouldOverrideUrlLoading}. */
+    public void setReturnValueForShouldOverrideUrlLoading(boolean value) {
+      returnValueForShouldOverrideUrlLoading = value;
     }
   }
 
@@ -117,29 +95,22 @@
    * Implementation of {@link WebViewClientCompat} that passes arguments of callback methods to
    * Dart.
    */
-  public static class WebViewClientCompatImpl extends WebViewClientCompat
-      implements ReleasableWebViewClient {
-    private @Nullable WebViewClientFlutterApiImpl flutterApi;
-    private final boolean shouldOverrideUrlLoading;
+  public static class WebViewClientCompatImpl extends WebViewClientCompat {
+    private final WebViewClientFlutterApiImpl flutterApi;
+    private boolean returnValueForShouldOverrideUrlLoading = false;
 
-    public WebViewClientCompatImpl(
-        @NonNull WebViewClientFlutterApiImpl flutterApi, boolean shouldOverrideUrlLoading) {
-      this.shouldOverrideUrlLoading = shouldOverrideUrlLoading;
+    public WebViewClientCompatImpl(@NonNull WebViewClientFlutterApiImpl flutterApi) {
       this.flutterApi = flutterApi;
     }
 
     @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,36 +122,28 @@
         @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 -> {});
-      }
-      return shouldOverrideUrlLoading;
+      flutterApi.requestLoading(this, view, request, reply -> {});
+      return returnValueForShouldOverrideUrlLoading;
     }
 
     @Override
     public boolean shouldOverrideUrlLoading(WebView view, String url) {
-      if (flutterApi != null) {
-        flutterApi.urlLoading(this, view, url, reply -> {});
-      }
-      return shouldOverrideUrlLoading;
+      flutterApi.urlLoading(this, view, url, reply -> {});
+      return returnValueForShouldOverrideUrlLoading;
     }
 
     @Override
@@ -190,11 +153,9 @@
       // truly lost.
     }
 
-    public void release() {
-      if (flutterApi != null) {
-        flutterApi.dispose(this, reply -> {});
-      }
-      flutterApi = null;
+    /** Sets return value for {@link #shouldOverrideUrlLoading}. */
+    public void setReturnValueForShouldOverrideUrlLoading(boolean value) {
+      returnValueForShouldOverrideUrlLoading = value;
     }
   }
 
@@ -206,8 +167,7 @@
      * @param flutterApi handles sending messages to Dart
      * @return the created {@link WebViewClient}
      */
-    public WebViewClient createWebViewClient(
-        WebViewClientFlutterApiImpl flutterApi, boolean shouldOverrideUrlLoading) {
+    public WebViewClient createWebViewClient(WebViewClientFlutterApiImpl flutterApi) {
       // WebViewClientCompat is used to get
       // shouldOverrideUrlLoading(WebView view, WebResourceRequest request)
       // invoked by the webview on older Android devices, without it pages that use iframes will
@@ -217,9 +177,9 @@
       // to bug https://bugs.chromium.org/p/chromium/issues/detail?id=925887. Also, see
       // https://github.com/flutter/flutter/issues/29446.
       if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
-        return new WebViewClientImpl(flutterApi, shouldOverrideUrlLoading);
+        return new WebViewClientImpl(flutterApi);
       } else {
-        return new WebViewClientCompatImpl(flutterApi, shouldOverrideUrlLoading);
+        return new WebViewClientCompatImpl(flutterApi);
       }
     }
   }
@@ -241,9 +201,24 @@
   }
 
   @Override
-  public void create(Long instanceId, Boolean shouldOverrideUrlLoading) {
-    final WebViewClient webViewClient =
-        webViewClientCreator.createWebViewClient(flutterApi, shouldOverrideUrlLoading);
+  public void create(@NonNull Long instanceId) {
+    final WebViewClient webViewClient = webViewClientCreator.createWebViewClient(flutterApi);
     instanceManager.addDartCreatedInstance(webViewClient, instanceId);
   }
+
+  @Override
+  public void setSynchronousReturnValueForShouldOverrideUrlLoading(
+      @NonNull Long instanceId, @NonNull Boolean value) {
+    final WebViewClient webViewClient =
+        Objects.requireNonNull(instanceManager.getInstance(instanceId));
+    if (webViewClient instanceof WebViewClientCompatImpl) {
+      ((WebViewClientCompatImpl) webViewClient).setReturnValueForShouldOverrideUrlLoading(value);
+    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
+        && webViewClient instanceof WebViewClientImpl) {
+      ((WebViewClientImpl) webViewClient).setReturnValueForShouldOverrideUrlLoading(value);
+    } else {
+      throw new IllegalStateException(
+          "This WebViewClient doesn't support setting the returnValueForShouldOverrideUrlLoading.");
+    }
+  }
 }
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 fe7615c..1c5a550 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
@@ -17,6 +17,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;
@@ -77,15 +78,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,
@@ -93,6 +101,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 778ad61..2fd9905 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..3267291 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;
 
@@ -35,6 +33,8 @@
 
   @Mock public WebView mockWebView;
 
+  @Mock public WebViewClientCompatImpl mockWebViewClient;
+
   InstanceManager instanceManager;
   WebViewClientHostApiImpl hostApiImpl;
   WebViewClientCompatImpl webViewClient;
@@ -48,18 +48,15 @@
     final WebViewClientCreator webViewClientCreator =
         new WebViewClientCreator() {
           @Override
-          public WebViewClient createWebViewClient(
-              WebViewClientFlutterApiImpl flutterApi, boolean shouldOverrideUrlLoading) {
-            webViewClient =
-                (WebViewClientCompatImpl)
-                    super.createWebViewClient(flutterApi, shouldOverrideUrlLoading);
+          public WebViewClient createWebViewClient(WebViewClientFlutterApiImpl flutterApi) {
+            webViewClient = (WebViewClientCompatImpl) super.createWebViewClient(flutterApi);
             return webViewClient;
           }
         };
 
     hostApiImpl =
         new WebViewClientHostApiImpl(instanceManager, webViewClientCreator, mockFlutterApi);
-    hostApiImpl.create(1L, true);
+    hostApiImpl.create(1L);
   }
 
   @After
@@ -72,11 +69,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 +82,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 +89,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
@@ -125,4 +106,23 @@
         WebViewClientFlutterApiImpl.createWebResourceRequestData(mockRequest);
     assertEquals(data.getRequestHeaders(), new HashMap<String, String>());
   }
+
+  @Test
+  public void setReturnValueForShouldOverrideUrlLoading() {
+    final WebViewClientHostApiImpl webViewClientHostApi =
+        new WebViewClientHostApiImpl(
+            instanceManager,
+            new WebViewClientCreator() {
+              @Override
+              public WebViewClient createWebViewClient(WebViewClientFlutterApiImpl flutterApi) {
+                return mockWebViewClient;
+              }
+            },
+            mockFlutterApi);
+
+    instanceManager.addDartCreatedInstance(mockWebViewClient, 0);
+    webViewClientHostApi.setSynchronousReturnValueForShouldOverrideUrlLoading(0L, false);
+
+    verify(mockWebViewClient).setReturnValueForShouldOverrideUrlLoading(false);
+  }
 }
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/legacy/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter_android/example/integration_test/legacy/webview_flutter_test.dart
new file mode 100644
index 0000000..57d3399
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_android/example/integration_test/legacy/webview_flutter_test.dart
@@ -0,0 +1,1567 @@
+// 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.
+
+// This test is run using `flutter drive` by the CI (see /script/tool/README.md
+// in this repository for details on driving that tooling manually), but can
+// also be run using `flutter test` directly during development.
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231)
+// ignore: unnecessary_import
+import 'dart:typed_data';
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+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/src/webview_flutter_android_legacy.dart';
+import 'package:webview_flutter_android_example/legacy/navigation_decision.dart';
+import 'package:webview_flutter_android_example/legacy/navigation_request.dart';
+import 'package:webview_flutter_android_example/legacy/web_view.dart';
+import 'package:webview_flutter_platform_interface/src/webview_flutter_platform_interface_legacy.dart';
+
+Future<void> main() async {
+  IntegrationTestWidgetsFlutterBinding.ensureInitialized();
+
+  final HttpServer server = await HttpServer.bind(InternetAddress.anyIPv4, 0);
+  server.forEach((HttpRequest request) {
+    if (request.uri.path == '/hello.txt') {
+      request.response.writeln('Hello, world.');
+    } else if (request.uri.path == '/secondary.txt') {
+      request.response.writeln('How are you today?');
+    } else if (request.uri.path == '/headers') {
+      request.response.writeln('${request.headers}');
+    } else if (request.uri.path == '/favicon.ico') {
+      request.response.statusCode = HttpStatus.notFound;
+    } else {
+      fail('unexpected request: ${request.method} ${request.uri}');
+    }
+    request.response.close();
+  });
+  final String prefixUrl = 'http://${server.address.address}:${server.port}';
+  final String primaryUrl = '$prefixUrl/hello.txt';
+  final String secondaryUrl = '$prefixUrl/secondary.txt';
+  final String headersUrl = '$prefixUrl/headers';
+
+  testWidgets('initialUrl', (WidgetTester tester) async {
+    final Completer<WebViewController> controllerCompleter =
+        Completer<WebViewController>();
+    final Completer<void> pageFinishedCompleter = Completer<void>();
+    await tester.pumpWidget(
+      MaterialApp(
+        home: Directionality(
+          textDirection: TextDirection.ltr,
+          child: WebView(
+            key: GlobalKey(),
+            initialUrl: primaryUrl,
+            onWebViewCreated: (WebViewController controller) {
+              controllerCompleter.complete(controller);
+            },
+            onPageFinished: pageFinishedCompleter.complete,
+          ),
+        ),
+      ),
+    );
+
+    final WebViewController controller = await controllerCompleter.future;
+    await pageFinishedCompleter.future;
+
+    final String? currentUrl = await controller.currentUrl();
+    expect(currentUrl, primaryUrl);
+  });
+
+  testWidgets('loadUrl', (WidgetTester tester) async {
+    final Completer<WebViewController> controllerCompleter =
+        Completer<WebViewController>();
+    final StreamController<String> pageLoads = StreamController<String>();
+    await tester.pumpWidget(
+      Directionality(
+        textDirection: TextDirection.ltr,
+        child: WebView(
+          key: GlobalKey(),
+          initialUrl: primaryUrl,
+          onWebViewCreated: (WebViewController controller) {
+            controllerCompleter.complete(controller);
+          },
+          onPageFinished: (String url) {
+            pageLoads.add(url);
+          },
+        ),
+      ),
+    );
+    final WebViewController controller = await controllerCompleter.future;
+
+    await controller.loadUrl(secondaryUrl);
+    await expectLater(
+      pageLoads.stream.firstWhere((String url) => url == secondaryUrl),
+      completion(secondaryUrl),
+    );
+  });
+
+  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>();
+    await tester.pumpWidget(
+      Directionality(
+        textDirection: TextDirection.ltr,
+        child: WebView(
+          key: GlobalKey(),
+          initialUrl: primaryUrl,
+          onWebViewCreated: (WebViewController controller) {
+            controllerCompleter.complete(controller);
+          },
+          javascriptMode: JavascriptMode.unrestricted,
+        ),
+      ),
+    );
+    final WebViewController controller = await controllerCompleter.future;
+    final String result = await controller.evaluateJavascript('1 + 1');
+    expect(result, equals('2'));
+  });
+
+  testWidgets('loadUrl with headers', (WidgetTester tester) async {
+    final Completer<WebViewController> controllerCompleter =
+        Completer<WebViewController>();
+    final StreamController<String> pageStarts = StreamController<String>();
+    final StreamController<String> pageLoads = StreamController<String>();
+    await tester.pumpWidget(
+      Directionality(
+        textDirection: TextDirection.ltr,
+        child: WebView(
+          key: GlobalKey(),
+          initialUrl: primaryUrl,
+          onWebViewCreated: (WebViewController controller) {
+            controllerCompleter.complete(controller);
+          },
+          javascriptMode: JavascriptMode.unrestricted,
+          onPageStarted: (String url) {
+            pageStarts.add(url);
+          },
+          onPageFinished: (String url) {
+            pageLoads.add(url);
+          },
+        ),
+      ),
+    );
+    final WebViewController controller = await controllerCompleter.future;
+    final Map<String, String> headers = <String, String>{
+      'test_header': 'flutter_test_header'
+    };
+    await controller.loadUrl(headersUrl, headers: headers);
+    final String? currentUrl = await controller.currentUrl();
+    expect(currentUrl, headersUrl);
+
+    await pageStarts.stream.firstWhere((String url) => url == currentUrl);
+    await pageLoads.stream.firstWhere((String url) => url == currentUrl);
+
+    final String content = await controller
+        .runJavascriptReturningResult('document.documentElement.innerText');
+    expect(content.contains('flutter_test_header'), isTrue);
+  });
+
+  testWidgets('JavascriptChannel', (WidgetTester tester) async {
+    final Completer<WebViewController> controllerCompleter =
+        Completer<WebViewController>();
+    final Completer<void> pageStarted = Completer<void>();
+    final Completer<void> pageLoaded = Completer<void>();
+    final Completer<String> channelCompleter = Completer<String>();
+    await tester.pumpWidget(
+      Directionality(
+        textDirection: TextDirection.ltr,
+        child: WebView(
+          key: GlobalKey(),
+          // This is the data URL for: '<!DOCTYPE html>'
+          initialUrl:
+              'data:text/html;charset=utf-8;base64,PCFET0NUWVBFIGh0bWw+',
+          onWebViewCreated: (WebViewController controller) {
+            controllerCompleter.complete(controller);
+          },
+          javascriptMode: JavascriptMode.unrestricted,
+          javascriptChannels: <JavascriptChannel>{
+            JavascriptChannel(
+              name: 'Echo',
+              onMessageReceived: (JavascriptMessage message) {
+                channelCompleter.complete(message.message);
+              },
+            ),
+          },
+          onPageStarted: (String url) {
+            pageStarted.complete(null);
+          },
+          onPageFinished: (String url) {
+            pageLoaded.complete(null);
+          },
+        ),
+      ),
+    );
+    final WebViewController controller = await controllerCompleter.future;
+    await pageStarted.future;
+    await pageLoaded.future;
+
+    expect(channelCompleter.isCompleted, isFalse);
+    await controller.runJavascript('Echo.postMessage("hello");');
+
+    await expectLater(channelCompleter.future, completion('hello'));
+  });
+
+  testWidgets('resize webview', (WidgetTester tester) async {
+    final Completer<void> initialResizeCompleter = Completer<void>();
+    final Completer<void> buttonTapResizeCompleter = Completer<void>();
+    final Completer<void> onPageFinished = Completer<void>();
+
+    bool resizeButtonTapped = false;
+    await tester.pumpWidget(ResizableWebView(
+      onResize: (_) {
+        if (resizeButtonTapped) {
+          buttonTapResizeCompleter.complete();
+        } else {
+          initialResizeCompleter.complete();
+        }
+      },
+      onPageFinished: () => onPageFinished.complete(),
+    ));
+    await onPageFinished.future;
+    // Wait for a potential call to resize after page is loaded.
+    await initialResizeCompleter.future.timeout(
+      const Duration(seconds: 3),
+      onTimeout: () => null,
+    );
+
+    resizeButtonTapped = true;
+    await tester.tap(find.byKey(const ValueKey<String>('resizeButton')));
+    await tester.pumpAndSettle();
+    expect(buttonTapResizeCompleter.future, completes);
+  });
+
+  testWidgets('set custom userAgent', (WidgetTester tester) async {
+    final Completer<WebViewController> controllerCompleter1 =
+        Completer<WebViewController>();
+    final GlobalKey globalKey = GlobalKey();
+    await tester.pumpWidget(
+      Directionality(
+        textDirection: TextDirection.ltr,
+        child: WebView(
+          key: globalKey,
+          initialUrl: 'about:blank',
+          javascriptMode: JavascriptMode.unrestricted,
+          userAgent: 'Custom_User_Agent1',
+          onWebViewCreated: (WebViewController controller) {
+            controllerCompleter1.complete(controller);
+          },
+        ),
+      ),
+    );
+    final WebViewController controller1 = await controllerCompleter1.future;
+    final String customUserAgent1 = await _getUserAgent(controller1);
+    expect(customUserAgent1, 'Custom_User_Agent1');
+    // rebuild the WebView with a different user agent.
+    await tester.pumpWidget(
+      Directionality(
+        textDirection: TextDirection.ltr,
+        child: WebView(
+          key: globalKey,
+          initialUrl: 'about:blank',
+          javascriptMode: JavascriptMode.unrestricted,
+          userAgent: 'Custom_User_Agent2',
+        ),
+      ),
+    );
+
+    final String customUserAgent2 = await _getUserAgent(controller1);
+    expect(customUserAgent2, 'Custom_User_Agent2');
+  });
+
+  testWidgets('use default platform userAgent after webView is rebuilt',
+      (WidgetTester tester) async {
+    final Completer<WebViewController> controllerCompleter =
+        Completer<WebViewController>();
+    final GlobalKey globalKey = GlobalKey();
+    // Build the webView with no user agent to get the default platform user agent.
+    await tester.pumpWidget(
+      Directionality(
+        textDirection: TextDirection.ltr,
+        child: WebView(
+          key: globalKey,
+          initialUrl: primaryUrl,
+          javascriptMode: JavascriptMode.unrestricted,
+          onWebViewCreated: (WebViewController controller) {
+            controllerCompleter.complete(controller);
+          },
+        ),
+      ),
+    );
+    final WebViewController controller = await controllerCompleter.future;
+    final String defaultPlatformUserAgent = await _getUserAgent(controller);
+    // rebuild the WebView with a custom user agent.
+    await tester.pumpWidget(
+      Directionality(
+        textDirection: TextDirection.ltr,
+        child: WebView(
+          key: globalKey,
+          initialUrl: 'about:blank',
+          javascriptMode: JavascriptMode.unrestricted,
+          userAgent: 'Custom_User_Agent',
+        ),
+      ),
+    );
+    final String customUserAgent = await _getUserAgent(controller);
+    expect(customUserAgent, 'Custom_User_Agent');
+    // rebuilds the WebView with no user agent.
+    await tester.pumpWidget(
+      Directionality(
+        textDirection: TextDirection.ltr,
+        child: WebView(
+          key: globalKey,
+          initialUrl: 'about:blank',
+          javascriptMode: JavascriptMode.unrestricted,
+        ),
+      ),
+    );
+
+    final String customUserAgent2 = await _getUserAgent(controller);
+    expect(customUserAgent2, defaultPlatformUserAgent);
+  });
+
+  group('Video playback policy', () {
+    late String videoTestBase64;
+    setUpAll(() async {
+      final ByteData videoData =
+          await rootBundle.load('assets/sample_video.mp4');
+      final String base64VideoData =
+          base64Encode(Uint8List.view(videoData.buffer));
+      final String videoTest = '''
+        <!DOCTYPE html><html>
+        <head><title>Video auto play</title>
+          <script type="text/javascript">
+            function play() {
+              var video = document.getElementById("video");
+              video.play();
+              video.addEventListener('timeupdate', videoTimeUpdateHandler, false);
+            }
+            function videoTimeUpdateHandler(e) {
+              var video = document.getElementById("video");
+              VideoTestTime.postMessage(video.currentTime);
+            }
+            function isPaused() {
+              var video = document.getElementById("video");
+              return video.paused;
+            }
+            function isFullScreen() {
+              var video = document.getElementById("video");
+              return video.webkitDisplayingFullscreen;
+            }
+          </script>
+        </head>
+        <body onload="play();">
+        <video controls playsinline autoplay id="video">
+          <source src="data:video/mp4;charset=utf-8;base64,$base64VideoData">
+        </video>
+        </body>
+        </html>
+      ''';
+      videoTestBase64 = base64Encode(const Utf8Encoder().convert(videoTest));
+    });
+
+    testWidgets('Auto media playback', (WidgetTester tester) async {
+      Completer<WebViewController> controllerCompleter =
+          Completer<WebViewController>();
+      Completer<void> pageLoaded = Completer<void>();
+
+      await tester.pumpWidget(
+        Directionality(
+          textDirection: TextDirection.ltr,
+          child: WebView(
+            key: GlobalKey(),
+            initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64',
+            onWebViewCreated: (WebViewController controller) {
+              controllerCompleter.complete(controller);
+            },
+            javascriptMode: JavascriptMode.unrestricted,
+            onPageFinished: (String url) {
+              pageLoaded.complete(null);
+            },
+            initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow,
+          ),
+        ),
+      );
+      WebViewController controller = await controllerCompleter.future;
+      await pageLoaded.future;
+
+      String isPaused =
+          await controller.runJavascriptReturningResult('isPaused();');
+      expect(isPaused, _webviewBool(false));
+
+      controllerCompleter = Completer<WebViewController>();
+      pageLoaded = Completer<void>();
+
+      // We change the key to re-create a new webview as we change the initialMediaPlaybackPolicy
+      await tester.pumpWidget(
+        Directionality(
+          textDirection: TextDirection.ltr,
+          child: WebView(
+            key: GlobalKey(),
+            initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64',
+            onWebViewCreated: (WebViewController controller) {
+              controllerCompleter.complete(controller);
+            },
+            javascriptMode: JavascriptMode.unrestricted,
+            onPageFinished: (String url) {
+              pageLoaded.complete(null);
+            },
+          ),
+        ),
+      );
+
+      controller = await controllerCompleter.future;
+      await pageLoaded.future;
+
+      isPaused = await controller.runJavascriptReturningResult('isPaused();');
+      expect(isPaused, _webviewBool(true));
+    });
+
+    testWidgets('Changes to initialMediaPlaybackPolicy are ignored',
+        (WidgetTester tester) async {
+      final Completer<WebViewController> controllerCompleter =
+          Completer<WebViewController>();
+      Completer<void> pageLoaded = Completer<void>();
+
+      final GlobalKey key = GlobalKey();
+      await tester.pumpWidget(
+        Directionality(
+          textDirection: TextDirection.ltr,
+          child: WebView(
+            key: key,
+            initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64',
+            onWebViewCreated: (WebViewController controller) {
+              controllerCompleter.complete(controller);
+            },
+            javascriptMode: JavascriptMode.unrestricted,
+            onPageFinished: (String url) {
+              pageLoaded.complete(null);
+            },
+            initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow,
+          ),
+        ),
+      );
+      final WebViewController controller = await controllerCompleter.future;
+      await pageLoaded.future;
+
+      String isPaused =
+          await controller.runJavascriptReturningResult('isPaused();');
+      expect(isPaused, _webviewBool(false));
+
+      pageLoaded = Completer<void>();
+
+      await tester.pumpWidget(
+        Directionality(
+          textDirection: TextDirection.ltr,
+          child: WebView(
+            key: key,
+            initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64',
+            onWebViewCreated: (WebViewController controller) {
+              controllerCompleter.complete(controller);
+            },
+            javascriptMode: JavascriptMode.unrestricted,
+            onPageFinished: (String url) {
+              pageLoaded.complete(null);
+            },
+          ),
+        ),
+      );
+
+      await controller.reload();
+
+      await pageLoaded.future;
+
+      isPaused = await controller.runJavascriptReturningResult('isPaused();');
+      expect(isPaused, _webviewBool(false));
+    });
+
+    testWidgets('Video plays inline when allowsInlineMediaPlayback is true',
+        (WidgetTester tester) async {
+      final Completer<WebViewController> controllerCompleter =
+          Completer<WebViewController>();
+      final Completer<void> pageLoaded = Completer<void>();
+      final Completer<void> videoPlaying = Completer<void>();
+
+      await tester.pumpWidget(
+        Directionality(
+          textDirection: TextDirection.ltr,
+          child: WebView(
+            initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64',
+            onWebViewCreated: (WebViewController controller) {
+              controllerCompleter.complete(controller);
+            },
+            javascriptMode: JavascriptMode.unrestricted,
+            javascriptChannels: <JavascriptChannel>{
+              JavascriptChannel(
+                name: 'VideoTestTime',
+                onMessageReceived: (JavascriptMessage message) {
+                  final double currentTime = double.parse(message.message);
+                  // Let it play for at least 1 second to make sure the related video's properties are set.
+                  if (currentTime > 1 && !videoPlaying.isCompleted) {
+                    videoPlaying.complete(null);
+                  }
+                },
+              ),
+            },
+            onPageFinished: (String url) {
+              pageLoaded.complete(null);
+            },
+            initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow,
+            allowsInlineMediaPlayback: true,
+          ),
+        ),
+      );
+      final WebViewController controller = await controllerCompleter.future;
+      await pageLoaded.future;
+
+      // Pump once to trigger the video play.
+      await tester.pump();
+
+      // Makes sure we get the correct event that indicates the video is actually playing.
+      await videoPlaying.future;
+
+      final String fullScreen =
+          await controller.runJavascriptReturningResult('isFullScreen();');
+      expect(fullScreen, _webviewBool(false));
+    });
+  });
+
+  group('Audio playback policy', () {
+    late String audioTestBase64;
+    setUpAll(() async {
+      final ByteData audioData =
+          await rootBundle.load('assets/sample_audio.ogg');
+      final String base64AudioData =
+          base64Encode(Uint8List.view(audioData.buffer));
+      final String audioTest = '''
+        <!DOCTYPE html><html>
+        <head><title>Audio auto play</title>
+          <script type="text/javascript">
+            function play() {
+              var audio = document.getElementById("audio");
+              audio.play();
+            }
+            function isPaused() {
+              var audio = document.getElementById("audio");
+              return audio.paused;
+            }
+          </script>
+        </head>
+        <body onload="play();">
+        <audio controls id="audio">
+          <source src="data:audio/ogg;charset=utf-8;base64,$base64AudioData">
+        </audio>
+        </body>
+        </html>
+      ''';
+      audioTestBase64 = base64Encode(const Utf8Encoder().convert(audioTest));
+    });
+
+    testWidgets('Auto media playback', (WidgetTester tester) async {
+      Completer<WebViewController> controllerCompleter =
+          Completer<WebViewController>();
+      Completer<void> pageStarted = Completer<void>();
+      Completer<void> pageLoaded = Completer<void>();
+
+      await tester.pumpWidget(
+        Directionality(
+          textDirection: TextDirection.ltr,
+          child: WebView(
+            key: GlobalKey(),
+            initialUrl: 'data:text/html;charset=utf-8;base64,$audioTestBase64',
+            onWebViewCreated: (WebViewController controller) {
+              controllerCompleter.complete(controller);
+            },
+            javascriptMode: JavascriptMode.unrestricted,
+            onPageStarted: (String url) {
+              pageStarted.complete(null);
+            },
+            onPageFinished: (String url) {
+              pageLoaded.complete(null);
+            },
+            initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow,
+          ),
+        ),
+      );
+      WebViewController controller = await controllerCompleter.future;
+      await pageStarted.future;
+      await pageLoaded.future;
+
+      String isPaused =
+          await controller.runJavascriptReturningResult('isPaused();');
+      expect(isPaused, _webviewBool(false));
+
+      controllerCompleter = Completer<WebViewController>();
+      pageStarted = Completer<void>();
+      pageLoaded = Completer<void>();
+
+      // We change the key to re-create a new webview as we change the initialMediaPlaybackPolicy
+      await tester.pumpWidget(
+        Directionality(
+          textDirection: TextDirection.ltr,
+          child: WebView(
+            key: GlobalKey(),
+            initialUrl: 'data:text/html;charset=utf-8;base64,$audioTestBase64',
+            onWebViewCreated: (WebViewController controller) {
+              controllerCompleter.complete(controller);
+            },
+            javascriptMode: JavascriptMode.unrestricted,
+            onPageStarted: (String url) {
+              pageStarted.complete(null);
+            },
+            onPageFinished: (String url) {
+              pageLoaded.complete(null);
+            },
+          ),
+        ),
+      );
+
+      controller = await controllerCompleter.future;
+      await pageStarted.future;
+      await pageLoaded.future;
+
+      isPaused = await controller.runJavascriptReturningResult('isPaused();');
+      expect(isPaused, _webviewBool(true));
+    });
+
+    testWidgets('Changes to initialMediaPlaybackPolicy are ignored',
+        (WidgetTester tester) async {
+      final Completer<WebViewController> controllerCompleter =
+          Completer<WebViewController>();
+      Completer<void> pageStarted = Completer<void>();
+      Completer<void> pageLoaded = Completer<void>();
+
+      final GlobalKey key = GlobalKey();
+      await tester.pumpWidget(
+        Directionality(
+          textDirection: TextDirection.ltr,
+          child: WebView(
+            key: key,
+            initialUrl: 'data:text/html;charset=utf-8;base64,$audioTestBase64',
+            onWebViewCreated: (WebViewController controller) {
+              controllerCompleter.complete(controller);
+            },
+            javascriptMode: JavascriptMode.unrestricted,
+            onPageStarted: (String url) {
+              pageStarted.complete(null);
+            },
+            onPageFinished: (String url) {
+              pageLoaded.complete(null);
+            },
+            initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow,
+          ),
+        ),
+      );
+      final WebViewController controller = await controllerCompleter.future;
+      await pageStarted.future;
+      await pageLoaded.future;
+
+      String isPaused =
+          await controller.runJavascriptReturningResult('isPaused();');
+      expect(isPaused, _webviewBool(false));
+
+      pageStarted = Completer<void>();
+      pageLoaded = Completer<void>();
+
+      await tester.pumpWidget(
+        Directionality(
+          textDirection: TextDirection.ltr,
+          child: WebView(
+            key: key,
+            initialUrl: 'data:text/html;charset=utf-8;base64,$audioTestBase64',
+            onWebViewCreated: (WebViewController controller) {
+              controllerCompleter.complete(controller);
+            },
+            javascriptMode: JavascriptMode.unrestricted,
+            onPageStarted: (String url) {
+              pageStarted.complete(null);
+            },
+            onPageFinished: (String url) {
+              pageLoaded.complete(null);
+            },
+          ),
+        ),
+      );
+
+      await controller.reload();
+
+      await pageStarted.future;
+      await pageLoaded.future;
+
+      isPaused = await controller.runJavascriptReturningResult('isPaused();');
+      expect(isPaused, _webviewBool(false));
+    });
+  });
+
+  testWidgets('getTitle', (WidgetTester tester) async {
+    const String getTitleTest = '''
+        <!DOCTYPE html><html>
+        <head><title>Some title</title>
+        </head>
+        <body>
+        </body>
+        </html>
+      ''';
+    final String getTitleTestBase64 =
+        base64Encode(const Utf8Encoder().convert(getTitleTest));
+    final Completer<void> pageStarted = Completer<void>();
+    final Completer<void> pageLoaded = Completer<void>();
+    final Completer<WebViewController> controllerCompleter =
+        Completer<WebViewController>();
+
+    await tester.pumpWidget(
+      Directionality(
+        textDirection: TextDirection.ltr,
+        child: WebView(
+          initialUrl: 'data:text/html;charset=utf-8;base64,$getTitleTestBase64',
+          onWebViewCreated: (WebViewController controller) {
+            controllerCompleter.complete(controller);
+          },
+          onPageStarted: (String url) {
+            pageStarted.complete(null);
+          },
+          onPageFinished: (String url) {
+            pageLoaded.complete(null);
+          },
+        ),
+      ),
+    );
+
+    final WebViewController controller = await controllerCompleter.future;
+    await pageStarted.future;
+    await pageLoaded.future;
+
+    final String? title = await controller.getTitle();
+    expect(title, 'Some title');
+  });
+
+  group('Programmatic Scroll', () {
+    testWidgets('setAndGetScrollPosition', (WidgetTester tester) async {
+      const String scrollTestPage = '''
+        <!DOCTYPE html>
+        <html>
+          <head>
+            <style>
+              body {
+                height: 100%;
+                width: 100%;
+              }
+              #container{
+                width:5000px;
+                height:5000px;
+            }
+            </style>
+          </head>
+          <body>
+            <div id="container"/>
+          </body>
+        </html>
+      ''';
+
+      final String scrollTestPageBase64 =
+          base64Encode(const Utf8Encoder().convert(scrollTestPage));
+
+      final Completer<void> pageLoaded = Completer<void>();
+      final Completer<WebViewController> controllerCompleter =
+          Completer<WebViewController>();
+
+      await tester.pumpWidget(
+        Directionality(
+          textDirection: TextDirection.ltr,
+          child: WebView(
+            initialUrl:
+                'data:text/html;charset=utf-8;base64,$scrollTestPageBase64',
+            onWebViewCreated: (WebViewController controller) {
+              controllerCompleter.complete(controller);
+            },
+            onPageFinished: (String url) {
+              pageLoaded.complete(null);
+            },
+          ),
+        ),
+      );
+
+      final WebViewController controller = await controllerCompleter.future;
+      await pageLoaded.future;
+
+      await tester.pumpAndSettle(const Duration(seconds: 3));
+
+      int scrollPosX = await controller.getScrollX();
+      int scrollPosY = await controller.getScrollY();
+
+      // Check scrollTo()
+      const int X_SCROLL = 123;
+      const int Y_SCROLL = 321;
+      // Get the initial position; this ensures that scrollTo is actually
+      // changing something, but also gives the native view's scroll position
+      // time to settle.
+      expect(scrollPosX, isNot(X_SCROLL));
+      expect(scrollPosX, isNot(Y_SCROLL));
+
+      await controller.scrollTo(X_SCROLL, Y_SCROLL);
+      scrollPosX = await controller.getScrollX();
+      scrollPosY = await controller.getScrollY();
+      expect(scrollPosX, X_SCROLL);
+      expect(scrollPosY, Y_SCROLL);
+
+      // Check scrollBy() (on top of scrollTo())
+      await controller.scrollBy(X_SCROLL, Y_SCROLL);
+      scrollPosX = await controller.getScrollX();
+      scrollPosY = await controller.getScrollY();
+      expect(scrollPosX, X_SCROLL * 2);
+      expect(scrollPosY, Y_SCROLL * 2);
+    });
+  });
+
+  group('SurfaceAndroidWebView', () {
+    setUpAll(() {
+      WebView.platform = SurfaceAndroidWebView();
+    });
+
+    tearDownAll(() {
+      WebView.platform = AndroidWebView();
+    });
+
+    testWidgets('setAndGetScrollPosition', (WidgetTester tester) async {
+      const String scrollTestPage = '''
+        <!DOCTYPE html>
+        <html>
+          <head>
+            <style>
+              body {
+                height: 100%;
+                width: 100%;
+              }
+              #container{
+                width:5000px;
+                height:5000px;
+            }
+            </style>
+          </head>
+          <body>
+            <div id="container"/>
+          </body>
+        </html>
+      ''';
+
+      final String scrollTestPageBase64 =
+          base64Encode(const Utf8Encoder().convert(scrollTestPage));
+
+      final Completer<void> pageLoaded = Completer<void>();
+      final Completer<WebViewController> controllerCompleter =
+          Completer<WebViewController>();
+
+      await tester.pumpWidget(
+        Directionality(
+          textDirection: TextDirection.ltr,
+          child: WebView(
+            initialUrl:
+                'data:text/html;charset=utf-8;base64,$scrollTestPageBase64',
+            onWebViewCreated: (WebViewController controller) {
+              controllerCompleter.complete(controller);
+            },
+            onPageFinished: (String url) {
+              pageLoaded.complete(null);
+            },
+          ),
+        ),
+      );
+
+      final WebViewController controller = await controllerCompleter.future;
+      await pageLoaded.future;
+
+      await tester.pumpAndSettle(const Duration(seconds: 3));
+
+      // Check scrollTo()
+      const int X_SCROLL = 123;
+      const int Y_SCROLL = 321;
+
+      await controller.scrollTo(X_SCROLL, Y_SCROLL);
+      int scrollPosX = await controller.getScrollX();
+      int scrollPosY = await controller.getScrollY();
+      expect(X_SCROLL, scrollPosX);
+      expect(Y_SCROLL, scrollPosY);
+
+      // Check scrollBy() (on top of scrollTo())
+      await controller.scrollBy(X_SCROLL, Y_SCROLL);
+      scrollPosX = await controller.getScrollX();
+      scrollPosY = await controller.getScrollY();
+      expect(X_SCROLL * 2, scrollPosX);
+      expect(Y_SCROLL * 2, scrollPosY);
+    });
+
+    testWidgets('inputs are scrolled into view when focused',
+        (WidgetTester tester) async {
+      const String scrollTestPage = '''
+        <!DOCTYPE html>
+        <html>
+          <head>
+            <style>
+              input {
+                margin: 10000px 0;
+              }
+              #viewport {
+                position: fixed;
+                top:0;
+                bottom:0;
+                left:0;
+                right:0;
+              }
+            </style>
+          </head>
+          <body>
+            <div id="viewport"></div>
+            <input type="text" id="inputEl">
+          </body>
+        </html>
+      ''';
+
+      final String scrollTestPageBase64 =
+          base64Encode(const Utf8Encoder().convert(scrollTestPage));
+
+      final Completer<void> pageLoaded = Completer<void>();
+      final Completer<WebViewController> controllerCompleter =
+          Completer<WebViewController>();
+
+      await tester.runAsync(() async {
+        await tester.pumpWidget(
+          Directionality(
+            textDirection: TextDirection.ltr,
+            child: SizedBox(
+              width: 200,
+              height: 200,
+              child: WebView(
+                initialUrl:
+                    'data:text/html;charset=utf-8;base64,$scrollTestPageBase64',
+                onWebViewCreated: (WebViewController controller) {
+                  controllerCompleter.complete(controller);
+                },
+                onPageFinished: (String url) {
+                  pageLoaded.complete(null);
+                },
+                javascriptMode: JavascriptMode.unrestricted,
+              ),
+            ),
+          ),
+        );
+        await Future<void>.delayed(const Duration(milliseconds: 20));
+        await tester.pump();
+      });
+
+      final WebViewController controller = await controllerCompleter.future;
+      await pageLoaded.future;
+      final String viewportRectJSON = await _runJavaScriptReturningResult(
+          controller, 'JSON.stringify(viewport.getBoundingClientRect())');
+      final Map<String, dynamic> viewportRectRelativeToViewport =
+          jsonDecode(viewportRectJSON) as Map<String, dynamic>;
+
+      // Check that the input is originally outside of the viewport.
+
+      final String initialInputClientRectJSON =
+          await _runJavaScriptReturningResult(
+              controller, 'JSON.stringify(inputEl.getBoundingClientRect())');
+      final Map<String, dynamic> initialInputClientRectRelativeToViewport =
+          jsonDecode(initialInputClientRectJSON) as Map<String, dynamic>;
+
+      expect(
+          initialInputClientRectRelativeToViewport['bottom'] <=
+              viewportRectRelativeToViewport['bottom'],
+          isFalse);
+
+      await controller.runJavascript('inputEl.focus()');
+
+      // Check that focusing the input brought it into view.
+
+      final String lastInputClientRectJSON =
+          await _runJavaScriptReturningResult(
+              controller, 'JSON.stringify(inputEl.getBoundingClientRect())');
+      final Map<String, dynamic> lastInputClientRectRelativeToViewport =
+          jsonDecode(lastInputClientRectJSON) as Map<String, dynamic>;
+
+      expect(
+          lastInputClientRectRelativeToViewport['top'] >=
+              viewportRectRelativeToViewport['top'],
+          isTrue);
+      expect(
+          lastInputClientRectRelativeToViewport['bottom'] <=
+              viewportRectRelativeToViewport['bottom'],
+          isTrue);
+
+      expect(
+          lastInputClientRectRelativeToViewport['left'] >=
+              viewportRectRelativeToViewport['left'],
+          isTrue);
+      expect(
+          lastInputClientRectRelativeToViewport['right'] <=
+              viewportRectRelativeToViewport['right'],
+          isTrue);
+    });
+  });
+
+  group('NavigationDelegate', () {
+    const String blankPage = '<!DOCTYPE html><head></head><body></body></html>';
+    final String blankPageEncoded = 'data:text/html;charset=utf-8;base64,'
+        '${base64Encode(const Utf8Encoder().convert(blankPage))}';
+
+    testWidgets('can allow requests', (WidgetTester tester) async {
+      final Completer<WebViewController> controllerCompleter =
+          Completer<WebViewController>();
+      final StreamController<String> pageLoads =
+          StreamController<String>.broadcast();
+      await tester.pumpWidget(
+        Directionality(
+          textDirection: TextDirection.ltr,
+          child: WebView(
+            key: GlobalKey(),
+            initialUrl: blankPageEncoded,
+            onWebViewCreated: (WebViewController controller) {
+              controllerCompleter.complete(controller);
+            },
+            javascriptMode: JavascriptMode.unrestricted,
+            navigationDelegate: (NavigationRequest request) {
+              return (request.url.contains('youtube.com'))
+                  ? NavigationDecision.prevent
+                  : NavigationDecision.navigate;
+            },
+            onPageFinished: (String url) => pageLoads.add(url),
+          ),
+        ),
+      );
+
+      await pageLoads.stream.first; // Wait for initial page load.
+      final WebViewController controller = await controllerCompleter.future;
+      await controller.runJavascript('location.href = "$secondaryUrl"');
+
+      await pageLoads.stream.first; // Wait for the next page load.
+      final String? currentUrl = await controller.currentUrl();
+      expect(currentUrl, secondaryUrl);
+    });
+
+    testWidgets('onWebResourceError', (WidgetTester tester) async {
+      final Completer<WebResourceError> errorCompleter =
+          Completer<WebResourceError>();
+
+      await tester.pumpWidget(
+        Directionality(
+          textDirection: TextDirection.ltr,
+          child: WebView(
+            key: GlobalKey(),
+            initialUrl: 'https://www.notawebsite..com',
+            onWebResourceError: (WebResourceError error) {
+              errorCompleter.complete(error);
+            },
+          ),
+        ),
+      );
+
+      final WebResourceError error = await errorCompleter.future;
+      expect(error, isNotNull);
+
+      expect(error.errorType, isNotNull);
+      expect(
+          error.failingUrl?.startsWith('https://www.notawebsite..com'), isTrue);
+    });
+
+    testWidgets('onWebResourceError is not called with valid url',
+        (WidgetTester tester) async {
+      final Completer<WebResourceError> errorCompleter =
+          Completer<WebResourceError>();
+      final Completer<void> pageFinishCompleter = Completer<void>();
+
+      await tester.pumpWidget(
+        Directionality(
+          textDirection: TextDirection.ltr,
+          child: WebView(
+            key: GlobalKey(),
+            initialUrl:
+                'data:text/html;charset=utf-8;base64,PCFET0NUWVBFIGh0bWw+',
+            onWebResourceError: (WebResourceError error) {
+              errorCompleter.complete(error);
+            },
+            onPageFinished: (_) => pageFinishCompleter.complete(),
+          ),
+        ),
+      );
+
+      expect(errorCompleter.future, doesNotComplete);
+      await pageFinishCompleter.future;
+    });
+
+    testWidgets(
+      'onWebResourceError only called for main frame',
+      (WidgetTester tester) async {
+        const String iframeTest = '''
+        <!DOCTYPE html>
+        <html>
+        <head>
+          <title>WebResourceError test</title>
+        </head>
+        <body>
+          <iframe src="https://notawebsite..com"></iframe>
+        </body>
+        </html>
+       ''';
+        final String iframeTestBase64 =
+            base64Encode(const Utf8Encoder().convert(iframeTest));
+
+        final Completer<WebResourceError> errorCompleter =
+            Completer<WebResourceError>();
+        final Completer<void> pageFinishCompleter = Completer<void>();
+
+        await tester.pumpWidget(
+          Directionality(
+            textDirection: TextDirection.ltr,
+            child: WebView(
+              key: GlobalKey(),
+              initialUrl:
+                  'data:text/html;charset=utf-8;base64,$iframeTestBase64',
+              onWebResourceError: (WebResourceError error) {
+                errorCompleter.complete(error);
+              },
+              onPageFinished: (_) => pageFinishCompleter.complete(),
+            ),
+          ),
+        );
+
+        expect(errorCompleter.future, doesNotComplete);
+        await pageFinishCompleter.future;
+      },
+    );
+
+    testWidgets('can block requests', (WidgetTester tester) async {
+      final Completer<WebViewController> controllerCompleter =
+          Completer<WebViewController>();
+      final StreamController<String> pageLoads =
+          StreamController<String>.broadcast();
+      await tester.pumpWidget(
+        Directionality(
+          textDirection: TextDirection.ltr,
+          child: WebView(
+            key: GlobalKey(),
+            initialUrl: blankPageEncoded,
+            onWebViewCreated: (WebViewController controller) {
+              controllerCompleter.complete(controller);
+            },
+            javascriptMode: JavascriptMode.unrestricted,
+            navigationDelegate: (NavigationRequest request) {
+              return (request.url.contains('youtube.com'))
+                  ? NavigationDecision.prevent
+                  : NavigationDecision.navigate;
+            },
+            onPageFinished: (String url) => pageLoads.add(url),
+          ),
+        ),
+      );
+
+      await pageLoads.stream.first; // Wait for initial page load.
+      final WebViewController controller = await controllerCompleter.future;
+      await controller
+          .runJavascript('location.href = "https://www.youtube.com/"');
+
+      // There should never be any second page load, since our new URL is
+      // blocked. Still wait for a potential page change for some time in order
+      // to give the test a chance to fail.
+      await pageLoads.stream.first
+          .timeout(const Duration(milliseconds: 500), onTimeout: () => '');
+      final String? currentUrl = await controller.currentUrl();
+      expect(currentUrl, isNot(contains('youtube.com')));
+    });
+
+    testWidgets('supports asynchronous decisions', (WidgetTester tester) async {
+      final Completer<WebViewController> controllerCompleter =
+          Completer<WebViewController>();
+      final StreamController<String> pageLoads =
+          StreamController<String>.broadcast();
+      await tester.pumpWidget(
+        Directionality(
+          textDirection: TextDirection.ltr,
+          child: WebView(
+            key: GlobalKey(),
+            initialUrl: blankPageEncoded,
+            onWebViewCreated: (WebViewController controller) {
+              controllerCompleter.complete(controller);
+            },
+            javascriptMode: JavascriptMode.unrestricted,
+            navigationDelegate: (NavigationRequest request) async {
+              NavigationDecision decision = NavigationDecision.prevent;
+              decision = await Future<NavigationDecision>.delayed(
+                  const Duration(milliseconds: 10),
+                  () => NavigationDecision.navigate);
+              return decision;
+            },
+            onPageFinished: (String url) => pageLoads.add(url),
+          ),
+        ),
+      );
+
+      await pageLoads.stream.first; // Wait for initial page load.
+      final WebViewController controller = await controllerCompleter.future;
+      await controller.runJavascript('location.href = "$secondaryUrl"');
+
+      await pageLoads.stream.first; // Wait for second page to load.
+      final String? currentUrl = await controller.currentUrl();
+      expect(currentUrl, secondaryUrl);
+    });
+  });
+
+  testWidgets('launches with gestureNavigationEnabled on iOS',
+      (WidgetTester tester) async {
+    final Completer<WebViewController> controllerCompleter =
+        Completer<WebViewController>();
+    await tester.pumpWidget(
+      Directionality(
+        textDirection: TextDirection.ltr,
+        child: SizedBox(
+          width: 400,
+          height: 300,
+          child: WebView(
+            key: GlobalKey(),
+            initialUrl: primaryUrl,
+            gestureNavigationEnabled: true,
+            onWebViewCreated: (WebViewController controller) {
+              controllerCompleter.complete(controller);
+            },
+          ),
+        ),
+      ),
+    );
+    final WebViewController controller = await controllerCompleter.future;
+    final String? currentUrl = await controller.currentUrl();
+    expect(currentUrl, primaryUrl);
+  });
+
+  testWidgets('target _blank opens in same window',
+      (WidgetTester tester) async {
+    final Completer<WebViewController> controllerCompleter =
+        Completer<WebViewController>();
+    final Completer<void> pageLoaded = Completer<void>();
+    await tester.pumpWidget(
+      Directionality(
+        textDirection: TextDirection.ltr,
+        child: WebView(
+          key: GlobalKey(),
+          onWebViewCreated: (WebViewController controller) {
+            controllerCompleter.complete(controller);
+          },
+          javascriptMode: JavascriptMode.unrestricted,
+          onPageFinished: (String url) {
+            pageLoaded.complete(null);
+          },
+        ),
+      ),
+    );
+    final WebViewController controller = await controllerCompleter.future;
+    await controller.runJavascript('window.open("$primaryUrl", "_blank")');
+    await pageLoaded.future;
+    final String? currentUrl = await controller.currentUrl();
+    expect(currentUrl, primaryUrl);
+  });
+
+  testWidgets(
+    'can open new window and go back',
+    (WidgetTester tester) async {
+      final Completer<WebViewController> controllerCompleter =
+          Completer<WebViewController>();
+      Completer<void> pageLoaded = Completer<void>();
+      await tester.pumpWidget(
+        Directionality(
+          textDirection: TextDirection.ltr,
+          child: WebView(
+            key: GlobalKey(),
+            onWebViewCreated: (WebViewController controller) {
+              controllerCompleter.complete(controller);
+            },
+            javascriptMode: JavascriptMode.unrestricted,
+            onPageFinished: (String url) {
+              pageLoaded.complete();
+            },
+            initialUrl: primaryUrl,
+          ),
+        ),
+      );
+      final WebViewController controller = await controllerCompleter.future;
+      expect(controller.currentUrl(), completion(primaryUrl));
+      await pageLoaded.future;
+      pageLoaded = Completer<void>();
+
+      await controller.runJavascript('window.open("$secondaryUrl")');
+      await pageLoaded.future;
+      pageLoaded = Completer<void>();
+      expect(controller.currentUrl(), completion(secondaryUrl));
+
+      expect(controller.canGoBack(), completion(true));
+      await controller.goBack();
+      await pageLoaded.future;
+      await expectLater(controller.currentUrl(), completion(primaryUrl));
+    },
+  );
+
+  testWidgets(
+    'JavaScript does not run in parent window',
+    (WidgetTester tester) async {
+      const String iframe = '''
+        <!DOCTYPE html>
+        <script>
+          window.onload = () => {
+            window.open(`javascript:
+              var elem = document.createElement("p");
+              elem.innerHTML = "<b>Executed JS in parent origin: " + window.location.origin + "</b>";
+              document.body.append(elem);
+            `);
+          };
+        </script>
+      ''';
+      final String iframeTestBase64 =
+          base64Encode(const Utf8Encoder().convert(iframe));
+
+      final String openWindowTest = '''
+        <!DOCTYPE html>
+        <html>
+        <head>
+          <title>XSS test</title>
+        </head>
+        <body>
+          <iframe
+            onload="window.iframeLoaded = true;"
+            src="data:text/html;charset=utf-8;base64,$iframeTestBase64"></iframe>
+        </body>
+        </html>
+      ''';
+      final String openWindowTestBase64 =
+          base64Encode(const Utf8Encoder().convert(openWindowTest));
+      final Completer<WebViewController> controllerCompleter =
+          Completer<WebViewController>();
+      final Completer<void> pageLoadCompleter = Completer<void>();
+
+      await tester.pumpWidget(
+        Directionality(
+          textDirection: TextDirection.ltr,
+          child: WebView(
+            key: GlobalKey(),
+            onWebViewCreated: (WebViewController controller) {
+              controllerCompleter.complete(controller);
+            },
+            javascriptMode: JavascriptMode.unrestricted,
+            initialUrl:
+                'data:text/html;charset=utf-8;base64,$openWindowTestBase64',
+            onPageFinished: (String url) {
+              pageLoadCompleter.complete();
+            },
+          ),
+        ),
+      );
+
+      final WebViewController controller = await controllerCompleter.future;
+      await pageLoadCompleter.future;
+
+      final String iframeLoaded =
+          await controller.runJavascriptReturningResult('iframeLoaded');
+      expect(iframeLoaded, 'true');
+
+      final String elementText = await controller.runJavascriptReturningResult(
+        'document.querySelector("p") && document.querySelector("p").textContent',
+      );
+      expect(elementText, 'null');
+    },
+  );
+
+  testWidgets(
+    'clearCache should clear local storage',
+    (WidgetTester tester) async {
+      final Completer<WebViewController> controllerCompleter =
+          Completer<WebViewController>();
+
+      Completer<void> pageLoadCompleter = Completer<void>();
+
+      await tester.pumpWidget(
+        Directionality(
+          textDirection: TextDirection.ltr,
+          child: WebView(
+            key: GlobalKey(),
+            initialUrl: primaryUrl,
+            javascriptMode: JavascriptMode.unrestricted,
+            onPageFinished: (_) => pageLoadCompleter.complete(),
+            onWebViewCreated: (WebViewController controller) {
+              controllerCompleter.complete(controller);
+            },
+          ),
+        ),
+      );
+
+      await pageLoadCompleter.future;
+      pageLoadCompleter = Completer<void>();
+
+      final WebViewController controller = await controllerCompleter.future;
+      await controller.runJavascript('localStorage.setItem("myCat", "Tom");');
+      final String myCatItem = await controller.runJavascriptReturningResult(
+        'localStorage.getItem("myCat");',
+      );
+      expect(myCatItem, '"Tom"');
+
+      await controller.clearCache();
+      await pageLoadCompleter.future;
+
+      final String nullItem = await controller.runJavascriptReturningResult(
+        'localStorage.getItem("myCat");',
+      );
+      expect(nullItem, 'null');
+    },
+  );
+}
+
+// JavaScript booleans evaluate to different string values on Android and iOS.
+// This utility method returns the string boolean value of the current platform.
+String _webviewBool(bool value) {
+  if (defaultTargetPlatform == TargetPlatform.iOS) {
+    return value ? '1' : '0';
+  }
+  return value ? 'true' : 'false';
+}
+
+/// Returns the value used for the HTTP User-Agent: request header in subsequent HTTP requests.
+Future<String> _getUserAgent(WebViewController controller) async {
+  return _runJavaScriptReturningResult(controller, 'navigator.userAgent;');
+}
+
+Future<String> _runJavaScriptReturningResult(
+  WebViewController controller,
+  String js,
+) async {
+  return jsonDecode(await controller.runJavascriptReturningResult(js))
+      as String;
+}
+
+class ResizableWebView extends StatefulWidget {
+  const ResizableWebView({
+    Key? key,
+    required this.onResize,
+    required this.onPageFinished,
+  }) : super(key: key);
+
+  final JavascriptMessageHandler onResize;
+  final VoidCallback onPageFinished;
+
+  @override
+  State<StatefulWidget> createState() => ResizableWebViewState();
+}
+
+class ResizableWebViewState extends State<ResizableWebView> {
+  double webViewWidth = 200;
+  double webViewHeight = 200;
+
+  static const String resizePage = '''
+        <!DOCTYPE html><html>
+        <head><title>Resize test</title>
+          <script type="text/javascript">
+            function onResize() {
+              Resize.postMessage("resize");
+            }
+            function onLoad() {
+              window.onresize = onResize;
+            }
+          </script>
+        </head>
+        <body onload="onLoad();" bgColor="blue">
+        </body>
+        </html>
+      ''';
+
+  @override
+  Widget build(BuildContext context) {
+    final String resizeTestBase64 =
+        base64Encode(const Utf8Encoder().convert(resizePage));
+    return Directionality(
+      textDirection: TextDirection.ltr,
+      child: Column(
+        children: <Widget>[
+          SizedBox(
+            width: webViewWidth,
+            height: webViewHeight,
+            child: WebView(
+              initialUrl:
+                  'data:text/html;charset=utf-8;base64,$resizeTestBase64',
+              javascriptChannels: <JavascriptChannel>{
+                JavascriptChannel(
+                  name: 'Resize',
+                  onMessageReceived: widget.onResize,
+                ),
+              },
+              onPageFinished: (_) => widget.onPageFinished(),
+              javascriptMode: JavascriptMode.unrestricted,
+            ),
+          ),
+          TextButton(
+            key: const Key('resizeButton'),
+            onPressed: () {
+              setState(() {
+                webViewWidth += 100.0;
+                webViewHeight += 100.0;
+              });
+            },
+            child: const Text('ResizeButton'),
+          ),
+        ],
+      ),
+    );
+  }
+}
+
+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/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart
index 69c1a46..2265aeb 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,11 +18,9 @@
 import 'package:flutter/services.dart';
 import 'package:flutter_test/flutter_test.dart';
 import 'package:integration_test/integration_test.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';
-import 'package:webview_flutter_android_example/navigation_request.dart';
-import 'package:webview_flutter_android_example/web_view.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_flutter_android.dart';
 import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
 
 Future<void> main() async {
@@ -48,164 +46,169 @@
   final String secondaryUrl = '$prefixUrl/secondary.txt';
   final String headersUrl = '$prefixUrl/headers';
 
-  testWidgets('initialUrl', (WidgetTester tester) async {
-    final Completer<WebViewController> controllerCompleter =
-        Completer<WebViewController>();
-    final Completer<void> pageFinishedCompleter = Completer<void>();
+  testWidgets('loadRequest', (WidgetTester tester) async {
+    final Completer<void> pageFinished = Completer<void>();
+
+    final PlatformWebViewController controller = PlatformWebViewController(
+      const PlatformWebViewControllerCreationParams(),
+    )
+      ..setPlatformNavigationDelegate(
+        PlatformNavigationDelegate(
+          const PlatformNavigationDelegateCreationParams(),
+        )..setOnPageFinished((_) => pageFinished.complete()),
+      )
+      ..loadRequest(LoadRequestParams(uri: Uri.parse(primaryUrl)));
+
     await tester.pumpWidget(
-      MaterialApp(
-        home: Directionality(
-          textDirection: TextDirection.ltr,
-          child: WebView(
-            key: GlobalKey(),
-            initialUrl: primaryUrl,
-            onWebViewCreated: (WebViewController controller) {
-              controllerCompleter.complete(controller);
-            },
-            onPageFinished: pageFinishedCompleter.complete,
-          ),
-        ),
+      Builder(
+        builder: (BuildContext context) {
+          return PlatformWebViewWidget(
+            PlatformWebViewWidgetCreationParams(controller: controller),
+          ).build(context);
+        },
       ),
     );
 
-    final WebViewController controller = await controllerCompleter.future;
-    await pageFinishedCompleter.future;
+    await pageFinished.future;
 
     final String? currentUrl = await controller.currentUrl();
     expect(currentUrl, primaryUrl);
   });
 
-  testWidgets('loadUrl', (WidgetTester tester) async {
-    final Completer<WebViewController> controllerCompleter =
-        Completer<WebViewController>();
-    final StreamController<String> pageLoads = StreamController<String>();
+  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('runJavaScriptReturningResult', (WidgetTester tester) async {
+    final Completer<void> pageFinished = Completer<void>();
+
+    final PlatformWebViewController controller = PlatformWebViewController(
+      const PlatformWebViewControllerCreationParams(),
+    )
+      ..setJavaScriptMode(JavaScriptMode.unrestricted)
+      ..setPlatformNavigationDelegate(
+        PlatformNavigationDelegate(
+          const PlatformNavigationDelegateCreationParams(),
+        )..setOnPageFinished((_) => pageFinished.complete()),
+      )
+      ..loadRequest(LoadRequestParams(uri: Uri.parse(primaryUrl)));
+
     await tester.pumpWidget(
-      Directionality(
-        textDirection: TextDirection.ltr,
-        child: WebView(
-          key: GlobalKey(),
-          initialUrl: primaryUrl,
-          onWebViewCreated: (WebViewController controller) {
-            controllerCompleter.complete(controller);
-          },
-          onPageFinished: (String url) {
-            pageLoads.add(url);
-          },
-        ),
+      Builder(
+        builder: (BuildContext context) {
+          return PlatformWebViewWidget(
+            PlatformWebViewWidgetCreationParams(controller: controller),
+          ).build(context);
+        },
       ),
     );
-    final WebViewController controller = await controllerCompleter.future;
 
-    await controller.loadUrl(secondaryUrl);
+    await pageFinished.future;
+
     await expectLater(
-      pageLoads.stream.firstWhere((String url) => url == secondaryUrl),
-      completion(secondaryUrl),
+      controller.runJavaScriptReturningResult('1 + 1'),
+      completion(2),
     );
   });
 
-  testWidgets('evaluateJavascript', (WidgetTester tester) async {
-    final Completer<WebViewController> controllerCompleter =
-        Completer<WebViewController>();
-    await tester.pumpWidget(
-      Directionality(
-        textDirection: TextDirection.ltr,
-        child: WebView(
-          key: GlobalKey(),
-          initialUrl: primaryUrl,
-          onWebViewCreated: (WebViewController controller) {
-            controllerCompleter.complete(controller);
-          },
-          javascriptMode: JavascriptMode.unrestricted,
-        ),
-      ),
-    );
-    final WebViewController controller = await controllerCompleter.future;
-    final String result = await controller.evaluateJavascript('1 + 1');
-    expect(result, equals('2'));
-  });
-
-  testWidgets('loadUrl with headers', (WidgetTester tester) async {
-    final Completer<WebViewController> controllerCompleter =
-        Completer<WebViewController>();
-    final StreamController<String> pageStarts = StreamController<String>();
-    final StreamController<String> pageLoads = StreamController<String>();
-    await tester.pumpWidget(
-      Directionality(
-        textDirection: TextDirection.ltr,
-        child: WebView(
-          key: GlobalKey(),
-          initialUrl: primaryUrl,
-          onWebViewCreated: (WebViewController controller) {
-            controllerCompleter.complete(controller);
-          },
-          javascriptMode: JavascriptMode.unrestricted,
-          onPageStarted: (String url) {
-            pageStarts.add(url);
-          },
-          onPageFinished: (String url) {
-            pageLoads.add(url);
-          },
-        ),
-      ),
-    );
-    final WebViewController controller = await controllerCompleter.future;
+  testWidgets('loadRequest with headers', (WidgetTester tester) async {
     final Map<String, String> headers = <String, String>{
       'test_header': 'flutter_test_header'
     };
-    await controller.loadUrl(headersUrl, headers: headers);
-    final String? currentUrl = await controller.currentUrl();
-    expect(currentUrl, headersUrl);
 
-    await pageStarts.stream.firstWhere((String url) => url == currentUrl);
-    await pageLoads.stream.firstWhere((String url) => url == currentUrl);
+    final StreamController<String> pageLoads = StreamController<String>();
 
-    final String content = await controller
-        .runJavascriptReturningResult('document.documentElement.innerText');
+    final PlatformWebViewController controller = PlatformWebViewController(
+      const PlatformWebViewControllerCreationParams(),
+    )
+      ..setJavaScriptMode(JavaScriptMode.unrestricted)
+      ..setPlatformNavigationDelegate(
+        PlatformNavigationDelegate(
+          const PlatformNavigationDelegateCreationParams(),
+        )..setOnPageFinished((String url) => pageLoads.add(url)),
+      )
+      ..loadRequest(
+        LoadRequestParams(
+          uri: Uri.parse(headersUrl),
+          headers: headers,
+        ),
+      );
+
+    await tester.pumpWidget(
+      Builder(
+        builder: (BuildContext context) {
+          return PlatformWebViewWidget(
+            PlatformWebViewWidgetCreationParams(controller: controller),
+          ).build(context);
+        },
+      ),
+    );
+
+    await pageLoads.stream.firstWhere((String url) => url == headersUrl);
+
+    final String content = await controller.runJavaScriptReturningResult(
+      'document.documentElement.innerText',
+    ) as String;
     expect(content.contains('flutter_test_header'), isTrue);
   });
 
   testWidgets('JavascriptChannel', (WidgetTester tester) async {
-    final Completer<WebViewController> controllerCompleter =
-        Completer<WebViewController>();
-    final Completer<void> pageStarted = Completer<void>();
-    final Completer<void> pageLoaded = Completer<void>();
+    final Completer<void> pageFinished = Completer<void>();
+    final PlatformWebViewController controller = PlatformWebViewController(
+      const PlatformWebViewControllerCreationParams(),
+    )
+      ..setJavaScriptMode(JavaScriptMode.unrestricted)
+      ..setPlatformNavigationDelegate(
+        PlatformNavigationDelegate(
+          const PlatformNavigationDelegateCreationParams(),
+        )..setOnPageFinished((_) => pageFinished.complete()),
+      );
+
     final Completer<String> channelCompleter = Completer<String>();
-    await tester.pumpWidget(
-      Directionality(
-        textDirection: TextDirection.ltr,
-        child: WebView(
-          key: GlobalKey(),
-          // This is the data URL for: '<!DOCTYPE html>'
-          initialUrl:
-              'data:text/html;charset=utf-8;base64,PCFET0NUWVBFIGh0bWw+',
-          onWebViewCreated: (WebViewController controller) {
-            controllerCompleter.complete(controller);
-          },
-          javascriptMode: JavascriptMode.unrestricted,
-          javascriptChannels: <JavascriptChannel>{
-            JavascriptChannel(
-              name: 'Echo',
-              onMessageReceived: (JavascriptMessage message) {
-                channelCompleter.complete(message.message);
-              },
-            ),
-          },
-          onPageStarted: (String url) {
-            pageStarted.complete(null);
-          },
-          onPageFinished: (String url) {
-            pageLoaded.complete(null);
-          },
-        ),
+    await controller.addJavaScriptChannel(
+      JavaScriptChannelParams(
+        name: 'Echo',
+        onMessageReceived: (JavaScriptMessage message) {
+          channelCompleter.complete(message.message);
+        },
       ),
     );
-    final WebViewController controller = await controllerCompleter.future;
-    await pageStarted.future;
-    await pageLoaded.future;
 
-    expect(channelCompleter.isCompleted, isFalse);
-    await controller.runJavascript('Echo.postMessage("hello");');
+    await controller.loadHtmlString(
+      'data:text/html;charset=utf-8;base64,PCFET0NUWVBFIGh0bWw+',
+    );
 
+    await tester.pumpWidget(
+      Builder(
+        builder: (BuildContext context) {
+          return PlatformWebViewWidget(
+            PlatformWebViewWidgetCreationParams(controller: controller),
+          ).build(context);
+        },
+      ),
+    );
+
+    await pageFinished.future;
+
+    await controller.runJavaScript('Echo.postMessage("hello");');
     await expectLater(channelCompleter.future, completion('hello'));
   });
 
@@ -216,7 +219,7 @@
 
     bool resizeButtonTapped = false;
     await tester.pumpWidget(ResizableWebView(
-      onResize: (_) {
+      onResize: () {
         if (resizeButtonTapped) {
           buttonTapResizeCompleter.complete();
         } else {
@@ -225,6 +228,7 @@
       },
       onPageFinished: () => onPageFinished.complete(),
     ));
+
     await onPageFinished.future;
     // Wait for a potential call to resize after page is loaded.
     await initialResizeCompleter.future.timeout(
@@ -233,98 +237,42 @@
     );
 
     resizeButtonTapped = true;
+
     await tester.tap(find.byKey(const ValueKey<String>('resizeButton')));
     await tester.pumpAndSettle();
-    expect(buttonTapResizeCompleter.future, completes);
+
+    await expectLater(buttonTapResizeCompleter.future, completes);
   });
 
   testWidgets('set custom userAgent', (WidgetTester tester) async {
-    final Completer<WebViewController> controllerCompleter1 =
-        Completer<WebViewController>();
-    final GlobalKey globalKey = GlobalKey();
+    final Completer<void> pageFinished = Completer<void>();
+
+    final PlatformWebViewController controller = PlatformWebViewController(
+      const PlatformWebViewControllerCreationParams(),
+    )
+      ..setJavaScriptMode(JavaScriptMode.unrestricted)
+      ..setPlatformNavigationDelegate(
+        PlatformNavigationDelegate(
+          const PlatformNavigationDelegateCreationParams(),
+        )..setOnPageFinished((_) => pageFinished.complete()),
+      )
+      ..setUserAgent('Custom_User_Agent1')
+      ..loadRequest(LoadRequestParams(uri: Uri.parse('about:blank')));
+
     await tester.pumpWidget(
-      Directionality(
-        textDirection: TextDirection.ltr,
-        child: WebView(
-          key: globalKey,
-          initialUrl: 'about:blank',
-          javascriptMode: JavascriptMode.unrestricted,
-          userAgent: 'Custom_User_Agent1',
-          onWebViewCreated: (WebViewController controller) {
-            controllerCompleter1.complete(controller);
-          },
-        ),
-      ),
-    );
-    final WebViewController controller1 = await controllerCompleter1.future;
-    final String customUserAgent1 = await _getUserAgent(controller1);
-    expect(customUserAgent1, 'Custom_User_Agent1');
-    // rebuild the WebView with a different user agent.
-    await tester.pumpWidget(
-      Directionality(
-        textDirection: TextDirection.ltr,
-        child: WebView(
-          key: globalKey,
-          initialUrl: 'about:blank',
-          javascriptMode: JavascriptMode.unrestricted,
-          userAgent: 'Custom_User_Agent2',
-        ),
+      Builder(
+        builder: (BuildContext context) {
+          return PlatformWebViewWidget(
+            PlatformWebViewWidgetCreationParams(controller: controller),
+          ).build(context);
+        },
       ),
     );
 
-    final String customUserAgent2 = await _getUserAgent(controller1);
-    expect(customUserAgent2, 'Custom_User_Agent2');
-  });
+    await pageFinished.future;
 
-  testWidgets('use default platform userAgent after webView is rebuilt',
-      (WidgetTester tester) async {
-    final Completer<WebViewController> controllerCompleter =
-        Completer<WebViewController>();
-    final GlobalKey globalKey = GlobalKey();
-    // Build the webView with no user agent to get the default platform user agent.
-    await tester.pumpWidget(
-      Directionality(
-        textDirection: TextDirection.ltr,
-        child: WebView(
-          key: globalKey,
-          initialUrl: primaryUrl,
-          javascriptMode: JavascriptMode.unrestricted,
-          onWebViewCreated: (WebViewController controller) {
-            controllerCompleter.complete(controller);
-          },
-        ),
-      ),
-    );
-    final WebViewController controller = await controllerCompleter.future;
-    final String defaultPlatformUserAgent = await _getUserAgent(controller);
-    // rebuild the WebView with a custom user agent.
-    await tester.pumpWidget(
-      Directionality(
-        textDirection: TextDirection.ltr,
-        child: WebView(
-          key: globalKey,
-          initialUrl: 'about:blank',
-          javascriptMode: JavascriptMode.unrestricted,
-          userAgent: 'Custom_User_Agent',
-        ),
-      ),
-    );
     final String customUserAgent = await _getUserAgent(controller);
-    expect(customUserAgent, 'Custom_User_Agent');
-    // rebuilds the WebView with no user agent.
-    await tester.pumpWidget(
-      Directionality(
-        textDirection: TextDirection.ltr,
-        child: WebView(
-          key: globalKey,
-          initialUrl: 'about:blank',
-          javascriptMode: JavascriptMode.unrestricted,
-        ),
-      ),
-    );
-
-    final String customUserAgent2 = await _getUserAgent(controller);
-    expect(customUserAgent2, defaultPlatformUserAgent);
+    expect(customUserAgent, 'Custom_User_Agent1');
   });
 
   group('Video playback policy', () {
@@ -335,201 +283,160 @@
       final String base64VideoData =
           base64Encode(Uint8List.view(videoData.buffer));
       final String videoTest = '''
-        <!DOCTYPE html><html>
-        <head><title>Video auto play</title>
-          <script type="text/javascript">
-            function play() {
-              var video = document.getElementById("video");
-              video.play();
-              video.addEventListener('timeupdate', videoTimeUpdateHandler, false);
-            }
-            function videoTimeUpdateHandler(e) {
-              var video = document.getElementById("video");
-              VideoTestTime.postMessage(video.currentTime);
-            }
-            function isPaused() {
-              var video = document.getElementById("video");
-              return video.paused;
-            }
-            function isFullScreen() {
-              var video = document.getElementById("video");
-              return video.webkitDisplayingFullscreen;
-            }
-          </script>
-        </head>
-        <body onload="play();">
-        <video controls playsinline autoplay id="video">
-          <source src="data:video/mp4;charset=utf-8;base64,$base64VideoData">
-        </video>
-        </body>
-        </html>
-      ''';
+          <!DOCTYPE html><html>
+          <head><title>Video auto play</title>
+            <script type="text/javascript">
+              function play() {
+                var video = document.getElementById("video");
+                video.play();
+                video.addEventListener('timeupdate', videoTimeUpdateHandler, false);
+              }
+              function videoTimeUpdateHandler(e) {
+                var video = document.getElementById("video");
+                VideoTestTime.postMessage(video.currentTime);
+              }
+              function isPaused() {
+                var video = document.getElementById("video");
+                return video.paused;
+              }
+              function isFullScreen() {
+                var video = document.getElementById("video");
+                return video.webkitDisplayingFullscreen;
+              }
+            </script>
+          </head>
+          <body onload="play();">
+          <video controls playsinline autoplay id="video">
+            <source src="data:video/mp4;charset=utf-8;base64,$base64VideoData">
+          </video>
+          </body>
+          </html>
+        ''';
       videoTestBase64 = base64Encode(const Utf8Encoder().convert(videoTest));
     });
 
     testWidgets('Auto media playback', (WidgetTester tester) async {
-      Completer<WebViewController> controllerCompleter =
-          Completer<WebViewController>();
       Completer<void> pageLoaded = Completer<void>();
 
-      await tester.pumpWidget(
-        Directionality(
-          textDirection: TextDirection.ltr,
-          child: WebView(
-            key: GlobalKey(),
-            initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64',
-            onWebViewCreated: (WebViewController controller) {
-              controllerCompleter.complete(controller);
-            },
-            javascriptMode: JavascriptMode.unrestricted,
-            onPageFinished: (String url) {
-              pageLoaded.complete(null);
-            },
-            initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow,
+      PlatformWebViewController controller = AndroidWebViewController(
+        const PlatformWebViewControllerCreationParams(),
+      )
+        ..setJavaScriptMode(JavaScriptMode.unrestricted)
+        ..setPlatformNavigationDelegate(
+          AndroidNavigationDelegate(
+            const PlatformNavigationDelegateCreationParams(),
+          )..setOnPageFinished((_) => pageLoaded.complete()),
+        )
+        ..setMediaPlaybackRequiresUserGesture(false)
+        ..loadRequest(
+          LoadRequestParams(
+            uri: Uri.parse(
+              'data:text/html;charset=utf-8;base64,$videoTestBase64',
+            ),
           ),
-        ),
-      );
-      WebViewController controller = await controllerCompleter.future;
-      await pageLoaded.future;
+        );
 
-      String isPaused =
-          await controller.runJavascriptReturningResult('isPaused();');
-      expect(isPaused, _webviewBool(false));
-
-      controllerCompleter = Completer<WebViewController>();
-      pageLoaded = Completer<void>();
-
-      // We change the key to re-create a new webview as we change the initialMediaPlaybackPolicy
       await tester.pumpWidget(
-        Directionality(
-          textDirection: TextDirection.ltr,
-          child: WebView(
-            key: GlobalKey(),
-            initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64',
-            onWebViewCreated: (WebViewController controller) {
-              controllerCompleter.complete(controller);
-            },
-            javascriptMode: JavascriptMode.unrestricted,
-            onPageFinished: (String url) {
-              pageLoaded.complete(null);
-            },
-          ),
+        Builder(
+          builder: (BuildContext context) {
+            return PlatformWebViewWidget(
+              PlatformWebViewWidgetCreationParams(controller: controller),
+            ).build(context);
+          },
         ),
       );
 
-      controller = await controllerCompleter.future;
       await pageLoaded.future;
 
-      isPaused = await controller.runJavascriptReturningResult('isPaused();');
-      expect(isPaused, _webviewBool(true));
-    });
-
-    testWidgets('Changes to initialMediaPlaybackPolicy are ignored',
-        (WidgetTester tester) async {
-      final Completer<WebViewController> controllerCompleter =
-          Completer<WebViewController>();
-      Completer<void> pageLoaded = Completer<void>();
-
-      final GlobalKey key = GlobalKey();
-      await tester.pumpWidget(
-        Directionality(
-          textDirection: TextDirection.ltr,
-          child: WebView(
-            key: key,
-            initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64',
-            onWebViewCreated: (WebViewController controller) {
-              controllerCompleter.complete(controller);
-            },
-            javascriptMode: JavascriptMode.unrestricted,
-            onPageFinished: (String url) {
-              pageLoaded.complete(null);
-            },
-            initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow,
-          ),
-        ),
-      );
-      final WebViewController controller = await controllerCompleter.future;
-      await pageLoaded.future;
-
-      String isPaused =
-          await controller.runJavascriptReturningResult('isPaused();');
-      expect(isPaused, _webviewBool(false));
+      bool isPaused =
+          await controller.runJavaScriptReturningResult('isPaused();') as bool;
+      expect(isPaused, false);
 
       pageLoaded = Completer<void>();
+      controller = PlatformWebViewController(
+        const PlatformWebViewControllerCreationParams(),
+      )
+        ..setJavaScriptMode(JavaScriptMode.unrestricted)
+        ..setPlatformNavigationDelegate(
+          PlatformNavigationDelegate(
+            const PlatformNavigationDelegateCreationParams(),
+          )..setOnPageFinished((_) => pageLoaded.complete()),
+        )
+        ..loadRequest(
+          LoadRequestParams(
+            uri: Uri.parse(
+              'data:text/html;charset=utf-8;base64,$videoTestBase64',
+            ),
+          ),
+        );
 
       await tester.pumpWidget(
-        Directionality(
-          textDirection: TextDirection.ltr,
-          child: WebView(
-            key: key,
-            initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64',
-            onWebViewCreated: (WebViewController controller) {
-              controllerCompleter.complete(controller);
-            },
-            javascriptMode: JavascriptMode.unrestricted,
-            onPageFinished: (String url) {
-              pageLoaded.complete(null);
-            },
-          ),
+        Builder(
+          builder: (BuildContext context) {
+            return PlatformWebViewWidget(
+              PlatformWebViewWidgetCreationParams(controller: controller),
+            ).build(context);
+          },
         ),
       );
 
-      await controller.reload();
-
       await pageLoaded.future;
 
-      isPaused = await controller.runJavascriptReturningResult('isPaused();');
-      expect(isPaused, _webviewBool(false));
+      isPaused =
+          await controller.runJavaScriptReturningResult('isPaused();') as bool;
+      expect(isPaused, true);
     });
 
-    testWidgets('Video plays inline when allowsInlineMediaPlayback is true',
-        (WidgetTester tester) async {
-      final Completer<WebViewController> controllerCompleter =
-          Completer<WebViewController>();
+    testWidgets('Video plays inline', (WidgetTester tester) async {
       final Completer<void> pageLoaded = Completer<void>();
       final Completer<void> videoPlaying = Completer<void>();
 
-      await tester.pumpWidget(
-        Directionality(
-          textDirection: TextDirection.ltr,
-          child: WebView(
-            initialUrl: 'data:text/html;charset=utf-8;base64,$videoTestBase64',
-            onWebViewCreated: (WebViewController controller) {
-              controllerCompleter.complete(controller);
+      final PlatformWebViewController controller = AndroidWebViewController(
+        const PlatformWebViewControllerCreationParams(),
+      )
+        ..setJavaScriptMode(JavaScriptMode.unrestricted)
+        ..setPlatformNavigationDelegate(
+          AndroidNavigationDelegate(
+            const PlatformNavigationDelegateCreationParams(),
+          )..setOnPageFinished((_) => pageLoaded.complete()),
+        )
+        ..addJavaScriptChannel(
+          JavaScriptChannelParams(
+            name: 'VideoTestTime',
+            onMessageReceived: (JavaScriptMessage message) {
+              final double currentTime = double.parse(message.message);
+              // Let it play for at least 1 second to make sure the related video's properties are set.
+              if (currentTime > 1 && !videoPlaying.isCompleted) {
+                videoPlaying.complete(null);
+              }
             },
-            javascriptMode: JavascriptMode.unrestricted,
-            javascriptChannels: <JavascriptChannel>{
-              JavascriptChannel(
-                name: 'VideoTestTime',
-                onMessageReceived: (JavascriptMessage message) {
-                  final double currentTime = double.parse(message.message);
-                  // Let it play for at least 1 second to make sure the related video's properties are set.
-                  if (currentTime > 1 && !videoPlaying.isCompleted) {
-                    videoPlaying.complete(null);
-                  }
-                },
-              ),
-            },
-            onPageFinished: (String url) {
-              pageLoaded.complete(null);
-            },
-            initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow,
-            allowsInlineMediaPlayback: true,
           ),
-        ),
-      );
-      final WebViewController controller = await controllerCompleter.future;
-      await pageLoaded.future;
+        )
+        ..setMediaPlaybackRequiresUserGesture(false)
+        ..loadRequest(
+          LoadRequestParams(
+            uri: Uri.parse(
+              'data:text/html;charset=utf-8;base64,$videoTestBase64',
+            ),
+          ),
+        );
 
-      // Pump once to trigger the video play.
-      await tester.pump();
+      await tester.pumpWidget(Builder(
+        builder: (BuildContext context) {
+          return PlatformWebViewWidget(
+            PlatformWebViewWidgetCreationParams(controller: controller),
+          ).build(context);
+        },
+      ));
+
+      await pageLoaded.future;
 
       // Makes sure we get the correct event that indicates the video is actually playing.
       await videoPlaying.future;
 
-      final String fullScreen =
-          await controller.runJavascriptReturningResult('isFullScreen();');
-      expect(fullScreen, _webviewBool(false));
+      final bool fullScreen = await controller
+          .runJavaScriptReturningResult('isFullScreen();') as bool;
+      expect(fullScreen, false);
     });
   });
 
@@ -565,138 +472,75 @@
     });
 
     testWidgets('Auto media playback', (WidgetTester tester) async {
-      Completer<WebViewController> controllerCompleter =
-          Completer<WebViewController>();
-      Completer<void> pageStarted = Completer<void>();
       Completer<void> pageLoaded = Completer<void>();
 
-      await tester.pumpWidget(
-        Directionality(
-          textDirection: TextDirection.ltr,
-          child: WebView(
-            key: GlobalKey(),
-            initialUrl: 'data:text/html;charset=utf-8;base64,$audioTestBase64',
-            onWebViewCreated: (WebViewController controller) {
-              controllerCompleter.complete(controller);
-            },
-            javascriptMode: JavascriptMode.unrestricted,
-            onPageStarted: (String url) {
-              pageStarted.complete(null);
-            },
-            onPageFinished: (String url) {
-              pageLoaded.complete(null);
-            },
-            initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow,
+      PlatformWebViewController controller = AndroidWebViewController(
+        const PlatformWebViewControllerCreationParams(),
+      )
+        ..setJavaScriptMode(JavaScriptMode.unrestricted)
+        ..setPlatformNavigationDelegate(
+          AndroidNavigationDelegate(
+            const PlatformNavigationDelegateCreationParams(),
+          )..setOnPageFinished((_) => pageLoaded.complete()),
+        )
+        ..setMediaPlaybackRequiresUserGesture(false)
+        ..loadRequest(
+          LoadRequestParams(
+            uri: Uri.parse(
+              'data:text/html;charset=utf-8;base64,$audioTestBase64',
+            ),
           ),
+        );
+
+      await tester.pumpWidget(
+        Builder(
+          builder: (BuildContext context) {
+            return PlatformWebViewWidget(
+              PlatformWebViewWidgetCreationParams(controller: controller),
+            ).build(context);
+          },
         ),
       );
-      WebViewController controller = await controllerCompleter.future;
-      await pageStarted.future;
+
       await pageLoaded.future;
 
-      String isPaused =
-          await controller.runJavascriptReturningResult('isPaused();');
-      expect(isPaused, _webviewBool(false));
+      bool isPaused =
+          await controller.runJavaScriptReturningResult('isPaused();') as bool;
+      expect(isPaused, false);
 
-      controllerCompleter = Completer<WebViewController>();
-      pageStarted = Completer<void>();
       pageLoaded = Completer<void>();
-
-      // We change the key to re-create a new webview as we change the initialMediaPlaybackPolicy
-      await tester.pumpWidget(
-        Directionality(
-          textDirection: TextDirection.ltr,
-          child: WebView(
-            key: GlobalKey(),
-            initialUrl: 'data:text/html;charset=utf-8;base64,$audioTestBase64',
-            onWebViewCreated: (WebViewController controller) {
-              controllerCompleter.complete(controller);
-            },
-            javascriptMode: JavascriptMode.unrestricted,
-            onPageStarted: (String url) {
-              pageStarted.complete(null);
-            },
-            onPageFinished: (String url) {
-              pageLoaded.complete(null);
-            },
+      controller = PlatformWebViewController(
+        const PlatformWebViewControllerCreationParams(),
+      )
+        ..setJavaScriptMode(JavaScriptMode.unrestricted)
+        ..setPlatformNavigationDelegate(
+          PlatformNavigationDelegate(
+            const PlatformNavigationDelegateCreationParams(),
+          )..setOnPageFinished((_) => pageLoaded.complete()),
+        )
+        ..loadRequest(
+          LoadRequestParams(
+            uri: Uri.parse(
+              'data:text/html;charset=utf-8;base64,$audioTestBase64',
+            ),
           ),
+        );
+
+      await tester.pumpWidget(
+        Builder(
+          builder: (BuildContext context) {
+            return PlatformWebViewWidget(
+              PlatformWebViewWidgetCreationParams(controller: controller),
+            ).build(context);
+          },
         ),
       );
 
-      controller = await controllerCompleter.future;
-      await pageStarted.future;
       await pageLoaded.future;
 
-      isPaused = await controller.runJavascriptReturningResult('isPaused();');
-      expect(isPaused, _webviewBool(true));
-    });
-
-    testWidgets('Changes to initialMediaPlaybackPolicy are ignored',
-        (WidgetTester tester) async {
-      final Completer<WebViewController> controllerCompleter =
-          Completer<WebViewController>();
-      Completer<void> pageStarted = Completer<void>();
-      Completer<void> pageLoaded = Completer<void>();
-
-      final GlobalKey key = GlobalKey();
-      await tester.pumpWidget(
-        Directionality(
-          textDirection: TextDirection.ltr,
-          child: WebView(
-            key: key,
-            initialUrl: 'data:text/html;charset=utf-8;base64,$audioTestBase64',
-            onWebViewCreated: (WebViewController controller) {
-              controllerCompleter.complete(controller);
-            },
-            javascriptMode: JavascriptMode.unrestricted,
-            onPageStarted: (String url) {
-              pageStarted.complete(null);
-            },
-            onPageFinished: (String url) {
-              pageLoaded.complete(null);
-            },
-            initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow,
-          ),
-        ),
-      );
-      final WebViewController controller = await controllerCompleter.future;
-      await pageStarted.future;
-      await pageLoaded.future;
-
-      String isPaused =
-          await controller.runJavascriptReturningResult('isPaused();');
-      expect(isPaused, _webviewBool(false));
-
-      pageStarted = Completer<void>();
-      pageLoaded = Completer<void>();
-
-      await tester.pumpWidget(
-        Directionality(
-          textDirection: TextDirection.ltr,
-          child: WebView(
-            key: key,
-            initialUrl: 'data:text/html;charset=utf-8;base64,$audioTestBase64',
-            onWebViewCreated: (WebViewController controller) {
-              controllerCompleter.complete(controller);
-            },
-            javascriptMode: JavascriptMode.unrestricted,
-            onPageStarted: (String url) {
-              pageStarted.complete(null);
-            },
-            onPageFinished: (String url) {
-              pageLoaded.complete(null);
-            },
-          ),
-        ),
-      );
-
-      await controller.reload();
-
-      await pageStarted.future;
-      await pageLoaded.future;
-
-      isPaused = await controller.runJavascriptReturningResult('isPaused();');
-      expect(isPaused, _webviewBool(false));
+      isPaused =
+          await controller.runJavaScriptReturningResult('isPaused();') as bool;
+      expect(isPaused, true);
     });
   });
 
@@ -711,33 +555,43 @@
       ''';
     final String getTitleTestBase64 =
         base64Encode(const Utf8Encoder().convert(getTitleTest));
-    final Completer<void> pageStarted = Completer<void>();
     final Completer<void> pageLoaded = Completer<void>();
-    final Completer<WebViewController> controllerCompleter =
-        Completer<WebViewController>();
+
+    final PlatformWebViewController controller = PlatformWebViewController(
+      const PlatformWebViewControllerCreationParams(),
+    )
+      ..setJavaScriptMode(JavaScriptMode.unrestricted)
+      ..setPlatformNavigationDelegate(
+        PlatformNavigationDelegate(
+          const PlatformNavigationDelegateCreationParams(),
+        )..setOnPageFinished((_) => pageLoaded.complete()),
+      )
+      ..loadRequest(
+        LoadRequestParams(
+          uri: Uri.parse(
+            'data:text/html;charset=utf-8;base64,$getTitleTestBase64',
+          ),
+        ),
+      );
 
     await tester.pumpWidget(
-      Directionality(
-        textDirection: TextDirection.ltr,
-        child: WebView(
-          initialUrl: 'data:text/html;charset=utf-8;base64,$getTitleTestBase64',
-          onWebViewCreated: (WebViewController controller) {
-            controllerCompleter.complete(controller);
-          },
-          onPageStarted: (String url) {
-            pageStarted.complete(null);
-          },
-          onPageFinished: (String url) {
-            pageLoaded.complete(null);
-          },
-        ),
+      Builder(
+        builder: (BuildContext context) {
+          return PlatformWebViewWidget(
+            PlatformWebViewWidgetCreationParams(controller: controller),
+          ).build(context);
+        },
       ),
     );
 
-    final WebViewController controller = await controllerCompleter.future;
-    await pageStarted.future;
     await pageLoaded.future;
 
+    // On at least iOS, it does not appear to be guaranteed that the native
+    // code has the title when the page load completes. Execute some JavaScript
+    // before checking the title to ensure that the page has been fully parsed
+    // and processed.
+    await controller.runJavaScript('1;');
+
     final String? title = await controller.getTitle();
     expect(title, 'Some title');
   });
@@ -769,32 +623,36 @@
           base64Encode(const Utf8Encoder().convert(scrollTestPage));
 
       final Completer<void> pageLoaded = Completer<void>();
-      final Completer<WebViewController> controllerCompleter =
-          Completer<WebViewController>();
-
-      await tester.pumpWidget(
-        Directionality(
-          textDirection: TextDirection.ltr,
-          child: WebView(
-            initialUrl:
-                'data:text/html;charset=utf-8;base64,$scrollTestPageBase64',
-            onWebViewCreated: (WebViewController controller) {
-              controllerCompleter.complete(controller);
-            },
-            onPageFinished: (String url) {
-              pageLoaded.complete(null);
-            },
+      final PlatformWebViewController controller = PlatformWebViewController(
+        const PlatformWebViewControllerCreationParams(),
+      )
+        ..setJavaScriptMode(JavaScriptMode.unrestricted)
+        ..setPlatformNavigationDelegate(
+          PlatformNavigationDelegate(
+            const PlatformNavigationDelegateCreationParams(),
+          )..setOnPageFinished((_) => pageLoaded.complete()),
+        )
+        ..loadRequest(
+          LoadRequestParams(
+            uri: Uri.parse(
+              'data:text/html;charset=utf-8;base64,$scrollTestPageBase64',
+            ),
           ),
-        ),
-      );
+        );
 
-      final WebViewController controller = await controllerCompleter.future;
+      await tester.pumpWidget(Builder(
+        builder: (BuildContext context) {
+          return PlatformWebViewWidget(
+            PlatformWebViewWidgetCreationParams(controller: controller),
+          ).build(context);
+        },
+      ));
+
       await pageLoaded.future;
 
       await tester.pumpAndSettle(const Duration(seconds: 3));
 
-      int scrollPosX = await controller.getScrollX();
-      int scrollPosY = await controller.getScrollY();
+      Offset scrollPos = await controller.getScrollPosition();
 
       // Check scrollTo()
       const int X_SCROLL = 123;
@@ -802,206 +660,19 @@
       // Get the initial position; this ensures that scrollTo is actually
       // changing something, but also gives the native view's scroll position
       // time to settle.
-      expect(scrollPosX, isNot(X_SCROLL));
-      expect(scrollPosX, isNot(Y_SCROLL));
+      expect(scrollPos.dx, isNot(X_SCROLL));
+      expect(scrollPos.dy, isNot(Y_SCROLL));
 
       await controller.scrollTo(X_SCROLL, Y_SCROLL);
-      scrollPosX = await controller.getScrollX();
-      scrollPosY = await controller.getScrollY();
-      expect(scrollPosX, X_SCROLL);
-      expect(scrollPosY, Y_SCROLL);
+      scrollPos = await controller.getScrollPosition();
+      expect(scrollPos.dx, X_SCROLL);
+      expect(scrollPos.dy, Y_SCROLL);
 
       // Check scrollBy() (on top of scrollTo())
       await controller.scrollBy(X_SCROLL, Y_SCROLL);
-      scrollPosX = await controller.getScrollX();
-      scrollPosY = await controller.getScrollY();
-      expect(scrollPosX, X_SCROLL * 2);
-      expect(scrollPosY, Y_SCROLL * 2);
-    });
-  });
-
-  group('SurfaceAndroidWebView', () {
-    setUpAll(() {
-      WebView.platform = SurfaceAndroidWebView();
-    });
-
-    tearDownAll(() {
-      WebView.platform = AndroidWebView();
-    });
-
-    testWidgets('setAndGetScrollPosition', (WidgetTester tester) async {
-      const String scrollTestPage = '''
-        <!DOCTYPE html>
-        <html>
-          <head>
-            <style>
-              body {
-                height: 100%;
-                width: 100%;
-              }
-              #container{
-                width:5000px;
-                height:5000px;
-            }
-            </style>
-          </head>
-          <body>
-            <div id="container"/>
-          </body>
-        </html>
-      ''';
-
-      final String scrollTestPageBase64 =
-          base64Encode(const Utf8Encoder().convert(scrollTestPage));
-
-      final Completer<void> pageLoaded = Completer<void>();
-      final Completer<WebViewController> controllerCompleter =
-          Completer<WebViewController>();
-
-      await tester.pumpWidget(
-        Directionality(
-          textDirection: TextDirection.ltr,
-          child: WebView(
-            initialUrl:
-                'data:text/html;charset=utf-8;base64,$scrollTestPageBase64',
-            onWebViewCreated: (WebViewController controller) {
-              controllerCompleter.complete(controller);
-            },
-            onPageFinished: (String url) {
-              pageLoaded.complete(null);
-            },
-          ),
-        ),
-      );
-
-      final WebViewController controller = await controllerCompleter.future;
-      await pageLoaded.future;
-
-      await tester.pumpAndSettle(const Duration(seconds: 3));
-
-      // Check scrollTo()
-      const int X_SCROLL = 123;
-      const int Y_SCROLL = 321;
-
-      await controller.scrollTo(X_SCROLL, Y_SCROLL);
-      int scrollPosX = await controller.getScrollX();
-      int scrollPosY = await controller.getScrollY();
-      expect(X_SCROLL, scrollPosX);
-      expect(Y_SCROLL, scrollPosY);
-
-      // Check scrollBy() (on top of scrollTo())
-      await controller.scrollBy(X_SCROLL, Y_SCROLL);
-      scrollPosX = await controller.getScrollX();
-      scrollPosY = await controller.getScrollY();
-      expect(X_SCROLL * 2, scrollPosX);
-      expect(Y_SCROLL * 2, scrollPosY);
-    });
-
-    testWidgets('inputs are scrolled into view when focused',
-        (WidgetTester tester) async {
-      const String scrollTestPage = '''
-        <!DOCTYPE html>
-        <html>
-          <head>
-            <style>
-              input {
-                margin: 10000px 0;
-              }
-              #viewport {
-                position: fixed;
-                top:0;
-                bottom:0;
-                left:0;
-                right:0;
-              }
-            </style>
-          </head>
-          <body>
-            <div id="viewport"></div>
-            <input type="text" id="inputEl">
-          </body>
-        </html>
-      ''';
-
-      final String scrollTestPageBase64 =
-          base64Encode(const Utf8Encoder().convert(scrollTestPage));
-
-      final Completer<void> pageLoaded = Completer<void>();
-      final Completer<WebViewController> controllerCompleter =
-          Completer<WebViewController>();
-
-      await tester.runAsync(() async {
-        await tester.pumpWidget(
-          Directionality(
-            textDirection: TextDirection.ltr,
-            child: SizedBox(
-              width: 200,
-              height: 200,
-              child: WebView(
-                initialUrl:
-                    'data:text/html;charset=utf-8;base64,$scrollTestPageBase64',
-                onWebViewCreated: (WebViewController controller) {
-                  controllerCompleter.complete(controller);
-                },
-                onPageFinished: (String url) {
-                  pageLoaded.complete(null);
-                },
-                javascriptMode: JavascriptMode.unrestricted,
-              ),
-            ),
-          ),
-        );
-        await Future<void>.delayed(const Duration(milliseconds: 20));
-        await tester.pump();
-      });
-
-      final WebViewController controller = await controllerCompleter.future;
-      await pageLoaded.future;
-      final String viewportRectJSON = await _runJavaScriptReturningResult(
-          controller, 'JSON.stringify(viewport.getBoundingClientRect())');
-      final Map<String, dynamic> viewportRectRelativeToViewport =
-          jsonDecode(viewportRectJSON) as Map<String, dynamic>;
-
-      // Check that the input is originally outside of the viewport.
-
-      final String initialInputClientRectJSON =
-          await _runJavaScriptReturningResult(
-              controller, 'JSON.stringify(inputEl.getBoundingClientRect())');
-      final Map<String, dynamic> initialInputClientRectRelativeToViewport =
-          jsonDecode(initialInputClientRectJSON) as Map<String, dynamic>;
-
-      expect(
-          initialInputClientRectRelativeToViewport['bottom'] <=
-              viewportRectRelativeToViewport['bottom'],
-          isFalse);
-
-      await controller.runJavascript('inputEl.focus()');
-
-      // Check that focusing the input brought it into view.
-
-      final String lastInputClientRectJSON =
-          await _runJavaScriptReturningResult(
-              controller, 'JSON.stringify(inputEl.getBoundingClientRect())');
-      final Map<String, dynamic> lastInputClientRectRelativeToViewport =
-          jsonDecode(lastInputClientRectJSON) as Map<String, dynamic>;
-
-      expect(
-          lastInputClientRectRelativeToViewport['top'] >=
-              viewportRectRelativeToViewport['top'],
-          isTrue);
-      expect(
-          lastInputClientRectRelativeToViewport['bottom'] <=
-              viewportRectRelativeToViewport['bottom'],
-          isTrue);
-
-      expect(
-          lastInputClientRectRelativeToViewport['left'] >=
-              viewportRectRelativeToViewport['left'],
-          isTrue);
-      expect(
-          lastInputClientRectRelativeToViewport['right'] <=
-              viewportRectRelativeToViewport['right'],
-          isTrue);
+      scrollPos = await controller.getScrollPosition();
+      expect(scrollPos.dx, X_SCROLL * 2);
+      expect(scrollPos.dy, Y_SCROLL * 2);
     });
   });
 
@@ -1011,33 +682,38 @@
         '${base64Encode(const Utf8Encoder().convert(blankPage))}';
 
     testWidgets('can allow requests', (WidgetTester tester) async {
-      final Completer<WebViewController> controllerCompleter =
-          Completer<WebViewController>();
       final StreamController<String> pageLoads =
           StreamController<String>.broadcast();
-      await tester.pumpWidget(
-        Directionality(
-          textDirection: TextDirection.ltr,
-          child: WebView(
-            key: GlobalKey(),
-            initialUrl: blankPageEncoded,
-            onWebViewCreated: (WebViewController controller) {
-              controllerCompleter.complete(controller);
-            },
-            javascriptMode: JavascriptMode.unrestricted,
-            navigationDelegate: (NavigationRequest request) {
-              return (request.url.contains('youtube.com'))
+
+      final PlatformWebViewController controller = PlatformWebViewController(
+        const PlatformWebViewControllerCreationParams(),
+      )
+        ..setJavaScriptMode(JavaScriptMode.unrestricted)
+        ..setPlatformNavigationDelegate(
+          PlatformNavigationDelegate(
+            const PlatformNavigationDelegateCreationParams(),
+          )
+            ..setOnPageFinished((String url) => pageLoads.add(url))
+            ..setOnNavigationRequest((NavigationRequest navigationRequest) {
+              return (navigationRequest.url.contains('youtube.com'))
                   ? NavigationDecision.prevent
                   : NavigationDecision.navigate;
-            },
-            onPageFinished: (String url) => pageLoads.add(url),
-          ),
-        ),
-      );
+            }),
+        )
+        ..loadRequest(
+          LoadRequestParams(uri: Uri.parse(blankPageEncoded)),
+        );
+
+      await tester.pumpWidget(Builder(
+        builder: (BuildContext context) {
+          return PlatformWebViewWidget(
+            PlatformWebViewWidgetCreationParams(controller: controller),
+          ).build(context);
+        },
+      ));
 
       await pageLoads.stream.first; // Wait for initial page load.
-      final WebViewController controller = await controllerCompleter.future;
-      await controller.runJavascript('location.href = "$secondaryUrl"');
+      await controller.runJavaScript('location.href = "$secondaryUrl"');
 
       await pageLoads.stream.first; // Wait for the next page load.
       final String? currentUrl = await controller.currentUrl();
@@ -1048,25 +724,39 @@
       final Completer<WebResourceError> errorCompleter =
           Completer<WebResourceError>();
 
-      await tester.pumpWidget(
-        Directionality(
-          textDirection: TextDirection.ltr,
-          child: WebView(
-            key: GlobalKey(),
-            initialUrl: 'https://www.notawebsite..com',
-            onWebResourceError: (WebResourceError error) {
+      final PlatformWebViewController controller = PlatformWebViewController(
+        const PlatformWebViewControllerCreationParams(),
+      )
+        ..setJavaScriptMode(JavaScriptMode.unrestricted)
+        ..setPlatformNavigationDelegate(
+          PlatformNavigationDelegate(
+            const PlatformNavigationDelegateCreationParams(),
+          )..setOnWebResourceError((WebResourceError error) {
               errorCompleter.complete(error);
-            },
-          ),
-        ),
-      );
+            }),
+        )
+        ..loadRequest(
+          LoadRequestParams(uri: Uri.parse('https://www.notawebsite..com')),
+        );
+
+      await tester.pumpWidget(Builder(
+        builder: (BuildContext context) {
+          return PlatformWebViewWidget(
+            PlatformWebViewWidgetCreationParams(controller: controller),
+          ).build(context);
+        },
+      ));
 
       final WebResourceError error = await errorCompleter.future;
       expect(error, isNotNull);
 
       expect(error.errorType, isNotNull);
       expect(
-          error.failingUrl?.startsWith('https://www.notawebsite..com'), isTrue);
+        (error as AndroidWebResourceError)
+            .failingUrl
+            ?.startsWith('https://www.notawebsite..com'),
+        isTrue,
+      );
     });
 
     testWidgets('onWebResourceError is not called with valid url',
@@ -1075,95 +765,71 @@
           Completer<WebResourceError>();
       final Completer<void> pageFinishCompleter = Completer<void>();
 
-      await tester.pumpWidget(
-        Directionality(
-          textDirection: TextDirection.ltr,
-          child: WebView(
-            key: GlobalKey(),
-            initialUrl:
-                'data:text/html;charset=utf-8;base64,PCFET0NUWVBFIGh0bWw+',
-            onWebResourceError: (WebResourceError error) {
+      final PlatformWebViewController controller = PlatformWebViewController(
+        const PlatformWebViewControllerCreationParams(),
+      )
+        ..setJavaScriptMode(JavaScriptMode.unrestricted)
+        ..setPlatformNavigationDelegate(
+          PlatformNavigationDelegate(
+            const PlatformNavigationDelegateCreationParams(),
+          )
+            ..setOnPageFinished((_) => pageFinishCompleter.complete())
+            ..setOnWebResourceError((WebResourceError error) {
               errorCompleter.complete(error);
-            },
-            onPageFinished: (_) => pageFinishCompleter.complete(),
+            }),
+        )
+        ..loadRequest(
+          LoadRequestParams(
+            uri: Uri.parse(
+              'data:text/html;charset=utf-8;base64,PCFET0NUWVBFIGh0bWw+',
+            ),
           ),
-        ),
-      );
+        );
+
+      await tester.pumpWidget(Builder(
+        builder: (BuildContext context) {
+          return PlatformWebViewWidget(
+            PlatformWebViewWidgetCreationParams(controller: controller),
+          ).build(context);
+        },
+      ));
 
       expect(errorCompleter.future, doesNotComplete);
       await pageFinishCompleter.future;
     });
 
-    testWidgets(
-      'onWebResourceError only called for main frame',
-      (WidgetTester tester) async {
-        const String iframeTest = '''
-        <!DOCTYPE html>
-        <html>
-        <head>
-          <title>WebResourceError test</title>
-        </head>
-        <body>
-          <iframe src="https://notawebsite..com"></iframe>
-        </body>
-        </html>
-       ''';
-        final String iframeTestBase64 =
-            base64Encode(const Utf8Encoder().convert(iframeTest));
-
-        final Completer<WebResourceError> errorCompleter =
-            Completer<WebResourceError>();
-        final Completer<void> pageFinishCompleter = Completer<void>();
-
-        await tester.pumpWidget(
-          Directionality(
-            textDirection: TextDirection.ltr,
-            child: WebView(
-              key: GlobalKey(),
-              initialUrl:
-                  'data:text/html;charset=utf-8;base64,$iframeTestBase64',
-              onWebResourceError: (WebResourceError error) {
-                errorCompleter.complete(error);
-              },
-              onPageFinished: (_) => pageFinishCompleter.complete(),
-            ),
-          ),
-        );
-
-        expect(errorCompleter.future, doesNotComplete);
-        await pageFinishCompleter.future;
-      },
-    );
-
     testWidgets('can block requests', (WidgetTester tester) async {
-      final Completer<WebViewController> controllerCompleter =
-          Completer<WebViewController>();
       final StreamController<String> pageLoads =
           StreamController<String>.broadcast();
-      await tester.pumpWidget(
-        Directionality(
-          textDirection: TextDirection.ltr,
-          child: WebView(
-            key: GlobalKey(),
-            initialUrl: blankPageEncoded,
-            onWebViewCreated: (WebViewController controller) {
-              controllerCompleter.complete(controller);
-            },
-            javascriptMode: JavascriptMode.unrestricted,
-            navigationDelegate: (NavigationRequest request) {
-              return (request.url.contains('youtube.com'))
+
+      final PlatformWebViewController controller = PlatformWebViewController(
+        const PlatformWebViewControllerCreationParams(),
+      )
+        ..setJavaScriptMode(JavaScriptMode.unrestricted)
+        ..setPlatformNavigationDelegate(
+          PlatformNavigationDelegate(
+            const PlatformNavigationDelegateCreationParams(),
+          )
+            ..setOnPageFinished((String url) => pageLoads.add(url))
+            ..setOnNavigationRequest((NavigationRequest navigationRequest) {
+              return (navigationRequest.url.contains('youtube.com'))
                   ? NavigationDecision.prevent
                   : NavigationDecision.navigate;
-            },
-            onPageFinished: (String url) => pageLoads.add(url),
-          ),
-        ),
-      );
+            }),
+        )
+        ..loadRequest(LoadRequestParams(uri: Uri.parse(blankPageEncoded)));
+
+      await tester.pumpWidget(Builder(
+        builder: (BuildContext context) {
+          return PlatformWebViewWidget(
+            PlatformWebViewWidgetCreationParams(controller: controller),
+          ).build(context);
+        },
+      ));
 
       await pageLoads.stream.first; // Wait for initial page load.
-      final WebViewController controller = await controllerCompleter.future;
       await controller
-          .runJavascript('location.href = "https://www.youtube.com/"');
+          .runJavaScript('location.href = "https://www.youtube.com/"');
 
       // There should never be any second page load, since our new URL is
       // blocked. Still wait for a potential page change for some time in order
@@ -1175,35 +841,39 @@
     });
 
     testWidgets('supports asynchronous decisions', (WidgetTester tester) async {
-      final Completer<WebViewController> controllerCompleter =
-          Completer<WebViewController>();
       final StreamController<String> pageLoads =
           StreamController<String>.broadcast();
-      await tester.pumpWidget(
-        Directionality(
-          textDirection: TextDirection.ltr,
-          child: WebView(
-            key: GlobalKey(),
-            initialUrl: blankPageEncoded,
-            onWebViewCreated: (WebViewController controller) {
-              controllerCompleter.complete(controller);
-            },
-            javascriptMode: JavascriptMode.unrestricted,
-            navigationDelegate: (NavigationRequest request) async {
+
+      final PlatformWebViewController controller = PlatformWebViewController(
+        const PlatformWebViewControllerCreationParams(),
+      )
+        ..setJavaScriptMode(JavaScriptMode.unrestricted)
+        ..setPlatformNavigationDelegate(
+          PlatformNavigationDelegate(
+            const PlatformNavigationDelegateCreationParams(),
+          )
+            ..setOnPageFinished((String url) => pageLoads.add(url))
+            ..setOnNavigationRequest(
+                (NavigationRequest navigationRequest) async {
               NavigationDecision decision = NavigationDecision.prevent;
               decision = await Future<NavigationDecision>.delayed(
                   const Duration(milliseconds: 10),
                   () => NavigationDecision.navigate);
               return decision;
-            },
-            onPageFinished: (String url) => pageLoads.add(url),
-          ),
-        ),
-      );
+            }),
+        )
+        ..loadRequest(LoadRequestParams(uri: Uri.parse(blankPageEncoded)));
+
+      await tester.pumpWidget(Builder(
+        builder: (BuildContext context) {
+          return PlatformWebViewWidget(
+            PlatformWebViewWidgetCreationParams(controller: controller),
+          ).build(context);
+        },
+      ));
 
       await pageLoads.stream.first; // Wait for initial page load.
-      final WebViewController controller = await controllerCompleter.future;
-      await controller.runJavascript('location.href = "$secondaryUrl"');
+      await controller.runJavaScript('location.href = "$secondaryUrl"');
 
       await pageLoads.stream.first; // Wait for second page to load.
       final String? currentUrl = await controller.currentUrl();
@@ -1211,54 +881,27 @@
     });
   });
 
-  testWidgets('launches with gestureNavigationEnabled on iOS',
-      (WidgetTester tester) async {
-    final Completer<WebViewController> controllerCompleter =
-        Completer<WebViewController>();
-    await tester.pumpWidget(
-      Directionality(
-        textDirection: TextDirection.ltr,
-        child: SizedBox(
-          width: 400,
-          height: 300,
-          child: WebView(
-            key: GlobalKey(),
-            initialUrl: primaryUrl,
-            gestureNavigationEnabled: true,
-            onWebViewCreated: (WebViewController controller) {
-              controllerCompleter.complete(controller);
-            },
-          ),
-        ),
-      ),
-    );
-    final WebViewController controller = await controllerCompleter.future;
-    final String? currentUrl = await controller.currentUrl();
-    expect(currentUrl, primaryUrl);
-  });
-
   testWidgets('target _blank opens in same window',
       (WidgetTester tester) async {
-    final Completer<WebViewController> controllerCompleter =
-        Completer<WebViewController>();
     final Completer<void> pageLoaded = Completer<void>();
-    await tester.pumpWidget(
-      Directionality(
-        textDirection: TextDirection.ltr,
-        child: WebView(
-          key: GlobalKey(),
-          onWebViewCreated: (WebViewController controller) {
-            controllerCompleter.complete(controller);
-          },
-          javascriptMode: JavascriptMode.unrestricted,
-          onPageFinished: (String url) {
-            pageLoaded.complete(null);
-          },
-        ),
-      ),
-    );
-    final WebViewController controller = await controllerCompleter.future;
-    await controller.runJavascript('window.open("$primaryUrl", "_blank")');
+
+    final PlatformWebViewController controller = PlatformWebViewController(
+      const PlatformWebViewControllerCreationParams(),
+    )
+      ..setJavaScriptMode(JavaScriptMode.unrestricted)
+      ..setPlatformNavigationDelegate(PlatformNavigationDelegate(
+        const PlatformNavigationDelegateCreationParams(),
+      )..setOnPageFinished((_) => pageLoaded.complete()));
+
+    await tester.pumpWidget(Builder(
+      builder: (BuildContext context) {
+        return PlatformWebViewWidget(
+          PlatformWebViewWidgetCreationParams(controller: controller),
+        ).build(context);
+      },
+    ));
+
+    await controller.runJavaScript('window.open("$primaryUrl", "_blank")');
     await pageLoaded.future;
     final String? currentUrl = await controller.currentUrl();
     expect(currentUrl, primaryUrl);
@@ -1267,31 +910,30 @@
   testWidgets(
     'can open new window and go back',
     (WidgetTester tester) async {
-      final Completer<WebViewController> controllerCompleter =
-          Completer<WebViewController>();
       Completer<void> pageLoaded = Completer<void>();
-      await tester.pumpWidget(
-        Directionality(
-          textDirection: TextDirection.ltr,
-          child: WebView(
-            key: GlobalKey(),
-            onWebViewCreated: (WebViewController controller) {
-              controllerCompleter.complete(controller);
-            },
-            javascriptMode: JavascriptMode.unrestricted,
-            onPageFinished: (String url) {
-              pageLoaded.complete();
-            },
-            initialUrl: primaryUrl,
-          ),
-        ),
-      );
-      final WebViewController controller = await controllerCompleter.future;
+
+      final PlatformWebViewController controller = PlatformWebViewController(
+        const PlatformWebViewControllerCreationParams(),
+      )
+        ..setJavaScriptMode(JavaScriptMode.unrestricted)
+        ..setPlatformNavigationDelegate(PlatformNavigationDelegate(
+          const PlatformNavigationDelegateCreationParams(),
+        )..setOnPageFinished((_) => pageLoaded.complete()))
+        ..loadRequest(LoadRequestParams(uri: Uri.parse(primaryUrl)));
+
+      await tester.pumpWidget(Builder(
+        builder: (BuildContext context) {
+          return PlatformWebViewWidget(
+            PlatformWebViewWidgetCreationParams(controller: controller),
+          ).build(context);
+        },
+      ));
+
       expect(controller.currentUrl(), completion(primaryUrl));
       await pageLoaded.future;
       pageLoaded = Completer<void>();
 
-      await controller.runJavascript('window.open("$secondaryUrl")');
+      await controller.runJavaScript('window.open("$secondaryUrl")');
       await pageLoaded.future;
       pageLoaded = Completer<void>();
       expect(controller.currentUrl(), completion(secondaryUrl));
@@ -1336,105 +978,56 @@
       ''';
       final String openWindowTestBase64 =
           base64Encode(const Utf8Encoder().convert(openWindowTest));
-      final Completer<WebViewController> controllerCompleter =
-          Completer<WebViewController>();
+
       final Completer<void> pageLoadCompleter = Completer<void>();
 
-      await tester.pumpWidget(
-        Directionality(
-          textDirection: TextDirection.ltr,
-          child: WebView(
-            key: GlobalKey(),
-            onWebViewCreated: (WebViewController controller) {
-              controllerCompleter.complete(controller);
-            },
-            javascriptMode: JavascriptMode.unrestricted,
-            initialUrl:
-                'data:text/html;charset=utf-8;base64,$openWindowTestBase64',
-            onPageFinished: (String url) {
-              pageLoadCompleter.complete();
-            },
+      final PlatformWebViewController controller = PlatformWebViewController(
+        const PlatformWebViewControllerCreationParams(),
+      )
+        ..setJavaScriptMode(JavaScriptMode.unrestricted)
+        ..setPlatformNavigationDelegate(PlatformNavigationDelegate(
+          const PlatformNavigationDelegateCreationParams(),
+        )..setOnPageFinished((_) => pageLoadCompleter.complete()))
+        ..loadRequest(
+          LoadRequestParams(
+            uri: Uri.parse(
+              'data:text/html;charset=utf-8;base64,$openWindowTestBase64',
+            ),
           ),
-        ),
-      );
+        );
 
-      final WebViewController controller = await controllerCompleter.future;
+      await tester.pumpWidget(Builder(
+        builder: (BuildContext context) {
+          return PlatformWebViewWidget(
+            PlatformWebViewWidgetCreationParams(controller: controller),
+          ).build(context);
+        },
+      ));
+
       await pageLoadCompleter.future;
 
-      final String iframeLoaded =
-          await controller.runJavascriptReturningResult('iframeLoaded');
-      expect(iframeLoaded, 'true');
+      final bool iframeLoaded =
+          await controller.runJavaScriptReturningResult('iframeLoaded') as bool;
+      expect(iframeLoaded, true);
 
-      final String elementText = await controller.runJavascriptReturningResult(
+      final String elementText = await controller.runJavaScriptReturningResult(
         'document.querySelector("p") && document.querySelector("p").textContent',
-      );
+      ) as String;
       expect(elementText, 'null');
     },
   );
-
-  testWidgets(
-    'clearCache should clear local storage',
-    (WidgetTester tester) async {
-      final Completer<WebViewController> controllerCompleter =
-          Completer<WebViewController>();
-
-      Completer<void> pageLoadCompleter = Completer<void>();
-
-      await tester.pumpWidget(
-        Directionality(
-          textDirection: TextDirection.ltr,
-          child: WebView(
-            key: GlobalKey(),
-            initialUrl: primaryUrl,
-            javascriptMode: JavascriptMode.unrestricted,
-            onPageFinished: (_) => pageLoadCompleter.complete(),
-            onWebViewCreated: (WebViewController controller) {
-              controllerCompleter.complete(controller);
-            },
-          ),
-        ),
-      );
-
-      await pageLoadCompleter.future;
-      pageLoadCompleter = Completer<void>();
-
-      final WebViewController controller = await controllerCompleter.future;
-      await controller.runJavascript('localStorage.setItem("myCat", "Tom");');
-      final String myCatItem = await controller.runJavascriptReturningResult(
-        'localStorage.getItem("myCat");',
-      );
-      expect(myCatItem, '"Tom"');
-
-      await controller.clearCache();
-      await pageLoadCompleter.future;
-
-      final String nullItem = await controller.runJavascriptReturningResult(
-        'localStorage.getItem("myCat");',
-      );
-      expect(nullItem, 'null');
-    },
-  );
-}
-
-// JavaScript booleans evaluate to different string values on Android and iOS.
-// This utility method returns the string boolean value of the current platform.
-String _webviewBool(bool value) {
-  if (defaultTargetPlatform == TargetPlatform.iOS) {
-    return value ? '1' : '0';
-  }
-  return value ? 'true' : 'false';
 }
 
 /// Returns the value used for the HTTP User-Agent: request header in subsequent HTTP requests.
-Future<String> _getUserAgent(WebViewController controller) async {
+Future<String> _getUserAgent(PlatformWebViewController controller) async {
   return _runJavaScriptReturningResult(controller, 'navigator.userAgent;');
 }
 
 Future<String> _runJavaScriptReturningResult(
-  WebViewController controller,
+  PlatformWebViewController controller,
   String js,
 ) async {
-  return jsonDecode(await controller.runJavascriptReturningResult(js))
+  return jsonDecode(await controller.runJavaScriptReturningResult(js) as String)
       as String;
 }
 
@@ -1445,7 +1038,7 @@
     required this.onPageFinished,
   }) : super(key: key);
 
-  final JavascriptMessageHandler onResize;
+  final VoidCallback onResize;
   final VoidCallback onPageFinished;
 
   @override
@@ -1453,6 +1046,31 @@
 }
 
 class ResizableWebViewState extends State<ResizableWebView> {
+  late final PlatformWebViewController controller = PlatformWebViewController(
+    const PlatformWebViewControllerCreationParams(),
+  )
+    ..setJavaScriptMode(JavaScriptMode.unrestricted)
+    ..setPlatformNavigationDelegate(
+      PlatformNavigationDelegate(
+        const PlatformNavigationDelegateCreationParams(),
+      )..setOnPageFinished((_) => widget.onPageFinished()),
+    )
+    ..addJavaScriptChannel(
+      JavaScriptChannelParams(
+        name: 'Resize',
+        onMessageReceived: (_) {
+          widget.onResize();
+        },
+      ),
+    )
+    ..loadRequest(
+      LoadRequestParams(
+        uri: Uri.parse(
+          'data:text/html;charset=utf-8;base64,${base64Encode(const Utf8Encoder().convert(resizePage))}',
+        ),
+      ),
+    );
+
   double webViewWidth = 200;
   double webViewHeight = 200;
 
@@ -1475,8 +1093,6 @@
 
   @override
   Widget build(BuildContext context) {
-    final String resizeTestBase64 =
-        base64Encode(const Utf8Encoder().convert(resizePage));
     return Directionality(
       textDirection: TextDirection.ltr,
       child: Column(
@@ -1484,18 +1100,9 @@
           SizedBox(
             width: webViewWidth,
             height: webViewHeight,
-            child: WebView(
-              initialUrl:
-                  'data:text/html;charset=utf-8;base64,$resizeTestBase64',
-              javascriptChannels: <JavascriptChannel>{
-                JavascriptChannel(
-                  name: 'Resize',
-                  onMessageReceived: widget.onResize,
-                ),
-              },
-              onPageFinished: (_) => widget.onPageFinished(),
-              javascriptMode: JavascriptMode.unrestricted,
-            ),
+            child: PlatformWebViewWidget(
+              PlatformWebViewWidgetCreationParams(controller: controller),
+            ).build(context),
           ),
           TextButton(
             key: const Key('resizeButton'),
@@ -1512,3 +1119,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/example/lib/navigation_decision.dart b/packages/webview_flutter/webview_flutter_android/example/lib/legacy/navigation_decision.dart
similarity index 100%
rename from packages/webview_flutter/webview_flutter_android/example/lib/navigation_decision.dart
rename to packages/webview_flutter/webview_flutter_android/example/lib/legacy/navigation_decision.dart
diff --git a/packages/webview_flutter/webview_flutter_android/example/lib/navigation_request.dart b/packages/webview_flutter/webview_flutter_android/example/lib/legacy/navigation_request.dart
similarity index 100%
rename from packages/webview_flutter/webview_flutter_android/example/lib/navigation_request.dart
rename to packages/webview_flutter/webview_flutter_android/example/lib/legacy/navigation_request.dart
diff --git a/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart b/packages/webview_flutter/webview_flutter_android/example/lib/legacy/web_view.dart
similarity index 98%
rename from packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart
rename to packages/webview_flutter/webview_flutter_android/example/lib/legacy/web_view.dart
index b7d98f2..b77a503 100644
--- a/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart
+++ b/packages/webview_flutter/webview_flutter_android/example/lib/legacy/web_view.dart
@@ -8,9 +8,10 @@
 import 'package:flutter/foundation.dart';
 import 'package:flutter/gestures.dart';
 import 'package:flutter/material.dart';
-import 'package:webview_flutter_android/webview_android.dart';
-import 'package:webview_flutter_android/webview_android_cookie_manager.dart';
-import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
+// ignore: implementation_imports
+import 'package:webview_flutter_android/src/webview_flutter_android_legacy.dart';
+// ignore: implementation_imports
+import 'package:webview_flutter_platform_interface/src/webview_flutter_platform_interface_legacy.dart';
 
 import 'navigation_decision.dart';
 import 'navigation_request.dart';
@@ -319,8 +320,10 @@
     required bool isForMainFrame,
   }) async {
     if (url.startsWith('https://www.youtube.com/')) {
+      debugPrint('blocking navigation to $url');
       return false;
     }
+    debugPrint('allowing navigation to $url');
     return true;
   }
 
diff --git a/packages/webview_flutter/webview_flutter_android/example/lib/main.dart b/packages/webview_flutter/webview_flutter_android/example/lib/main.dart
index bd958ed..fe6d723 100644
--- a/packages/webview_flutter/webview_flutter_android/example/lib/main.dart
+++ b/packages/webview_flutter/webview_flutter_android/example/lib/main.dart
@@ -2,33 +2,20 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// ignore_for_file: public_member_api_docs, avoid_print
+// ignore_for_file: public_member_api_docs
 
 import 'dart:async';
 import 'dart:convert';
 import 'dart:io';
 import 'dart:typed_data';
+
 import 'package:flutter/material.dart';
-import 'package:flutter_driver/driver_extension.dart';
 import 'package:path_provider/path_provider.dart';
-import 'package:webview_flutter_android/webview_surface_android.dart';
+import 'package:webview_flutter_android/webview_flutter_android.dart';
 import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
 
-import 'navigation_decision.dart';
-import 'navigation_request.dart';
-import 'web_view.dart';
-
-void appMain() {
-  enableFlutterDriverExtension();
-  main();
-}
-
 void main() {
-  // Configure the [WebView] to use the [SurfaceAndroidWebView]
-  // implementation instead of the default [AndroidWebView].
-  WebView.platform = SurfaceAndroidWebView();
-
-  runApp(const MaterialApp(home: _WebViewExample()));
+  runApp(const MaterialApp(home: WebViewExample()));
 }
 
 const String kNavigationExamplePage = '''
@@ -46,7 +33,7 @@
 </html>
 ''';
 
-const String kExamplePage = '''
+const String kLocalExamplePage = '''
 <!DOCTYPE html>
 <html lang="en">
 <head>
@@ -86,16 +73,70 @@
 </html>
 ''';
 
-class _WebViewExample extends StatefulWidget {
-  const _WebViewExample({Key? key}) : super(key: key);
+class WebViewExample extends StatefulWidget {
+  const WebViewExample({Key? key, this.cookieManager}) : super(key: key);
+
+  final PlatformWebViewCookieManager? cookieManager;
 
   @override
-  _WebViewExampleState createState() => _WebViewExampleState();
+  State<WebViewExample> createState() => _WebViewExampleState();
 }
 
-class _WebViewExampleState extends State<_WebViewExample> {
-  final Completer<WebViewController> _controller =
-      Completer<WebViewController>();
+class _WebViewExampleState extends State<WebViewExample> {
+  late final PlatformWebViewController _controller;
+
+  @override
+  void initState() {
+    super.initState();
+
+    _controller = PlatformWebViewController(
+      AndroidWebViewControllerCreationParams(),
+    )
+      ..setJavaScriptMode(JavaScriptMode.unrestricted)
+      ..setBackgroundColor(const Color(0x80000000))
+      ..setPlatformNavigationDelegate(
+        PlatformNavigationDelegate(
+          const PlatformNavigationDelegateCreationParams(),
+        )
+          ..setOnProgress((int progress) {
+            debugPrint('WebView is loading (progress : $progress%)');
+          })
+          ..setOnPageStarted((String url) {
+            debugPrint('Page started loading: $url');
+          })
+          ..setOnPageFinished((String url) {
+            debugPrint('Page finished loading: $url');
+          })
+          ..setOnWebResourceError((WebResourceError error) {
+            debugPrint('''
+Page resource error:
+  code: ${error.errorCode}
+  description: ${error.description}
+  errorType: ${error.errorType}
+  isForMainFrame: ${error.isForMainFrame}
+          ''');
+          })
+          ..setOnNavigationRequest((NavigationRequest request) {
+            if (request.url.startsWith('https://www.youtube.com/')) {
+              debugPrint('blocking navigation to ${request.url}');
+              return NavigationDecision.prevent;
+            }
+            debugPrint('allowing navigation to ${request.url}');
+            return NavigationDecision.navigate;
+          }),
+      )
+      ..addJavaScriptChannel(JavaScriptChannelParams(
+        name: 'Toaster',
+        onMessageReceived: (JavaScriptMessage message) {
+          ScaffoldMessenger.of(context).showSnackBar(
+            SnackBar(content: Text(message.message)),
+          );
+        },
+      ))
+      ..loadRequest(LoadRequestParams(
+        uri: Uri.parse('https://flutter.dev'),
+      ));
+  }
 
   @override
   Widget build(BuildContext context) {
@@ -105,74 +146,34 @@
         title: const Text('Flutter WebView example'),
         // This drop down menu demonstrates that Flutter widgets can be shown over the web view.
         actions: <Widget>[
-          _NavigationControls(_controller.future),
-          _SampleMenu(_controller.future),
+          NavigationControls(webViewController: _controller),
+          SampleMenu(
+            webViewController: _controller,
+            cookieManager: widget.cookieManager,
+          ),
         ],
       ),
-      body: WebView(
-        initialUrl: 'https://flutter.dev',
-        onWebViewCreated: (WebViewController controller) {
-          _controller.complete(controller);
-        },
-        onProgress: (int progress) {
-          print('WebView is loading (progress : $progress%)');
-        },
-        navigationDelegate: (NavigationRequest request) {
-          if (request.url.startsWith('https://www.youtube.com/')) {
-            print('blocking navigation to $request}');
-            return NavigationDecision.prevent;
-          }
-          print('allowing navigation to $request');
-          return NavigationDecision.navigate;
-        },
-        onPageStarted: (String url) {
-          print('Page started loading: $url');
-        },
-        onPageFinished: (String url) {
-          print('Page finished loading: $url');
-        },
-        javascriptChannels: _createJavascriptChannels(context),
-        javascriptMode: JavascriptMode.unrestricted,
-        userAgent: 'Custom_User_Agent',
-        backgroundColor: const Color(0x80000000),
-      ),
+      body: PlatformWebViewWidget(
+        PlatformWebViewWidgetCreationParams(controller: _controller),
+      ).build(context),
       floatingActionButton: favoriteButton(),
     );
   }
 
   Widget favoriteButton() {
-    return FutureBuilder<WebViewController>(
-        future: _controller.future,
-        builder: (BuildContext context,
-            AsyncSnapshot<WebViewController> controller) {
-          if (controller.hasData) {
-            return FloatingActionButton(
-              onPressed: () async {
-                final String url = (await controller.data!.currentUrl())!;
-                ScaffoldMessenger.of(context).showSnackBar(
-                  SnackBar(content: Text('Favorited $url')),
-                );
-              },
-              child: const Icon(Icons.favorite),
-            );
-          }
-          return Container();
-        });
+    return FloatingActionButton(
+      onPressed: () async {
+        final String? url = await _controller.currentUrl();
+        ScaffoldMessenger.of(context).showSnackBar(
+          SnackBar(content: Text('Favorited $url')),
+        );
+      },
+      child: const Icon(Icons.favorite),
+    );
   }
 }
 
-Set<JavascriptChannel> _createJavascriptChannels(BuildContext context) {
-  return <JavascriptChannel>{
-    JavascriptChannel(
-        name: 'Snackbar',
-        onMessageReceived: (JavascriptMessage message) {
-          ScaffoldMessenger.of(context)
-              .showSnackBar(SnackBar(content: Text(message.message)));
-        }),
-  };
-}
-
-enum _MenuOptions {
+enum MenuOptions {
   showUserAgent,
   listCookies,
   clearCookies,
@@ -180,143 +181,144 @@
   listCache,
   clearCache,
   navigationDelegate,
-  loadFlutterAsset,
+  doPostRequest,
   loadLocalFile,
+  loadFlutterAsset,
   loadHtmlString,
   transparentBackground,
-  doPostRequest,
   setCookie,
 }
 
-class _SampleMenu extends StatelessWidget {
-  const _SampleMenu(this.controller);
+class SampleMenu extends StatelessWidget {
+  SampleMenu({
+    Key? key,
+    required this.webViewController,
+    PlatformWebViewCookieManager? cookieManager,
+  })  : cookieManager = cookieManager ??
+            PlatformWebViewCookieManager(
+              const PlatformWebViewCookieManagerCreationParams(),
+            ),
+        super(key: key);
 
-  final Future<WebViewController> controller;
+  final PlatformWebViewController webViewController;
+  late final PlatformWebViewCookieManager cookieManager;
 
   @override
   Widget build(BuildContext context) {
-    return FutureBuilder<WebViewController>(
-      future: controller,
-      builder:
-          (BuildContext context, AsyncSnapshot<WebViewController> controller) {
-        return PopupMenuButton<_MenuOptions>(
-          key: const ValueKey<String>('ShowPopupMenu'),
-          onSelected: (_MenuOptions value) {
-            switch (value) {
-              case _MenuOptions.showUserAgent:
-                _onShowUserAgent(controller.data!, context);
-                break;
-              case _MenuOptions.listCookies:
-                _onListCookies(controller.data!, context);
-                break;
-              case _MenuOptions.clearCookies:
-                _onClearCookies(controller.data!, context);
-                break;
-              case _MenuOptions.addToCache:
-                _onAddToCache(controller.data!, context);
-                break;
-              case _MenuOptions.listCache:
-                _onListCache(controller.data!, context);
-                break;
-              case _MenuOptions.clearCache:
-                _onClearCache(controller.data!, context);
-                break;
-              case _MenuOptions.navigationDelegate:
-                _onNavigationDelegateExample(controller.data!, context);
-                break;
-              case _MenuOptions.loadFlutterAsset:
-                _onLoadFlutterAssetExample(controller.data!, context);
-                break;
-              case _MenuOptions.loadLocalFile:
-                _onLoadLocalFileExample(controller.data!, context);
-                break;
-              case _MenuOptions.loadHtmlString:
-                _onLoadHtmlStringExample(controller.data!, context);
-                break;
-              case _MenuOptions.transparentBackground:
-                _onTransparentBackground(controller.data!, context);
-                break;
-              case _MenuOptions.doPostRequest:
-                _onDoPostRequest(controller.data!, context);
-                break;
-              case _MenuOptions.setCookie:
-                _onSetCookie(controller.data!, context);
-                break;
-            }
-          },
-          itemBuilder: (BuildContext context) => <PopupMenuItem<_MenuOptions>>[
-            PopupMenuItem<_MenuOptions>(
-              value: _MenuOptions.showUserAgent,
-              enabled: controller.hasData,
-              child: const Text('Show user agent'),
-            ),
-            const PopupMenuItem<_MenuOptions>(
-              value: _MenuOptions.listCookies,
-              child: Text('List cookies'),
-            ),
-            const PopupMenuItem<_MenuOptions>(
-              value: _MenuOptions.clearCookies,
-              child: Text('Clear cookies'),
-            ),
-            const PopupMenuItem<_MenuOptions>(
-              value: _MenuOptions.addToCache,
-              child: Text('Add to cache'),
-            ),
-            const PopupMenuItem<_MenuOptions>(
-              value: _MenuOptions.listCache,
-              child: Text('List cache'),
-            ),
-            const PopupMenuItem<_MenuOptions>(
-              value: _MenuOptions.clearCache,
-              child: Text('Clear cache'),
-            ),
-            const PopupMenuItem<_MenuOptions>(
-              value: _MenuOptions.navigationDelegate,
-              child: Text('Navigation Delegate example'),
-            ),
-            const PopupMenuItem<_MenuOptions>(
-              value: _MenuOptions.loadFlutterAsset,
-              child: Text('Load Flutter Asset'),
-            ),
-            const PopupMenuItem<_MenuOptions>(
-              value: _MenuOptions.loadHtmlString,
-              child: Text('Load HTML string'),
-            ),
-            const PopupMenuItem<_MenuOptions>(
-              value: _MenuOptions.loadLocalFile,
-              child: Text('Load local file'),
-            ),
-            const PopupMenuItem<_MenuOptions>(
-              key: ValueKey<String>('ShowTransparentBackgroundExample'),
-              value: _MenuOptions.transparentBackground,
-              child: Text('Transparent background example'),
-            ),
-            const PopupMenuItem<_MenuOptions>(
-              value: _MenuOptions.doPostRequest,
-              child: Text('Post Request'),
-            ),
-            const PopupMenuItem<_MenuOptions>(
-              value: _MenuOptions.setCookie,
-              child: Text('Set Cookie'),
-            ),
-          ],
-        );
+    return PopupMenuButton<MenuOptions>(
+      key: const ValueKey<String>('ShowPopupMenu'),
+      onSelected: (MenuOptions value) {
+        switch (value) {
+          case MenuOptions.showUserAgent:
+            _onShowUserAgent();
+            break;
+          case MenuOptions.listCookies:
+            _onListCookies(context);
+            break;
+          case MenuOptions.clearCookies:
+            _onClearCookies(context);
+            break;
+          case MenuOptions.addToCache:
+            _onAddToCache(context);
+            break;
+          case MenuOptions.listCache:
+            _onListCache();
+            break;
+          case MenuOptions.clearCache:
+            _onClearCache(context);
+            break;
+          case MenuOptions.navigationDelegate:
+            _onNavigationDelegateExample();
+            break;
+          case MenuOptions.doPostRequest:
+            _onDoPostRequest();
+            break;
+          case MenuOptions.loadLocalFile:
+            _onLoadLocalFileExample();
+            break;
+          case MenuOptions.loadFlutterAsset:
+            _onLoadFlutterAssetExample();
+            break;
+          case MenuOptions.loadHtmlString:
+            _onLoadHtmlStringExample();
+            break;
+          case MenuOptions.transparentBackground:
+            _onTransparentBackground();
+            break;
+          case MenuOptions.setCookie:
+            _onSetCookie();
+            break;
+        }
       },
+      itemBuilder: (BuildContext context) => <PopupMenuItem<MenuOptions>>[
+        const PopupMenuItem<MenuOptions>(
+          value: MenuOptions.showUserAgent,
+          child: Text('Show user agent'),
+        ),
+        const PopupMenuItem<MenuOptions>(
+          value: MenuOptions.listCookies,
+          child: Text('List cookies'),
+        ),
+        const PopupMenuItem<MenuOptions>(
+          value: MenuOptions.clearCookies,
+          child: Text('Clear cookies'),
+        ),
+        const PopupMenuItem<MenuOptions>(
+          value: MenuOptions.addToCache,
+          child: Text('Add to cache'),
+        ),
+        const PopupMenuItem<MenuOptions>(
+          value: MenuOptions.listCache,
+          child: Text('List cache'),
+        ),
+        const PopupMenuItem<MenuOptions>(
+          value: MenuOptions.clearCache,
+          child: Text('Clear cache'),
+        ),
+        const PopupMenuItem<MenuOptions>(
+          value: MenuOptions.navigationDelegate,
+          child: Text('Navigation Delegate example'),
+        ),
+        const PopupMenuItem<MenuOptions>(
+          value: MenuOptions.doPostRequest,
+          child: Text('Post Request'),
+        ),
+        const PopupMenuItem<MenuOptions>(
+          value: MenuOptions.loadHtmlString,
+          child: Text('Load HTML string'),
+        ),
+        const PopupMenuItem<MenuOptions>(
+          value: MenuOptions.loadLocalFile,
+          child: Text('Load local file'),
+        ),
+        const PopupMenuItem<MenuOptions>(
+          value: MenuOptions.loadFlutterAsset,
+          child: Text('Load Flutter Asset'),
+        ),
+        const PopupMenuItem<MenuOptions>(
+          value: MenuOptions.setCookie,
+          child: Text('Set cookie'),
+        ),
+        const PopupMenuItem<MenuOptions>(
+          key: ValueKey<String>('ShowTransparentBackgroundExample'),
+          value: MenuOptions.transparentBackground,
+          child: Text('Transparent background example'),
+        ),
+      ],
     );
   }
 
-  Future<void> _onShowUserAgent(
-      WebViewController controller, BuildContext context) async {
-    // Send a message with the user agent string to the Snackbar JavaScript channel we registered
+  Future<void> _onShowUserAgent() {
+    // Send a message with the user agent string to the Toaster JavaScript channel we registered
     // with the WebView.
-    await controller.runJavascript(
-        'Snackbar.postMessage("User Agent: " + navigator.userAgent);');
+    return webViewController.runJavaScript(
+      'Toaster.postMessage("User Agent: " + navigator.userAgent);',
+    );
   }
 
-  Future<void> _onListCookies(
-      WebViewController controller, BuildContext context) async {
-    final String cookies =
-        await controller.runJavascriptReturningResult('document.cookie');
+  Future<void> _onListCookies(BuildContext context) async {
+    final String cookies = await webViewController
+        .runJavaScriptReturningResult('document.cookie') as String;
     ScaffoldMessenger.of(context).showSnackBar(SnackBar(
       content: Column(
         mainAxisAlignment: MainAxisAlignment.end,
@@ -329,34 +331,32 @@
     ));
   }
 
-  Future<void> _onAddToCache(
-      WebViewController controller, BuildContext context) async {
-    await controller.runJavascript(
-        'caches.open("test_caches_entry"); localStorage["test_localStorage"] = "dummy_entry";');
+  Future<void> _onAddToCache(BuildContext context) async {
+    await webViewController.runJavaScript(
+      'caches.open("test_caches_entry"); localStorage["test_localStorage"] = "dummy_entry";',
+    );
     ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
       content: Text('Added a test entry to cache.'),
     ));
   }
 
-  Future<void> _onListCache(
-      WebViewController controller, BuildContext context) async {
-    await controller.runJavascript('caches.keys()'
+  Future<void> _onListCache() {
+    return webViewController.runJavaScript('caches.keys()'
         // ignore: missing_whitespace_between_adjacent_strings
         '.then((cacheKeys) => JSON.stringify({"cacheKeys" : cacheKeys, "localStorage" : localStorage}))'
-        '.then((caches) => Snackbar.postMessage(caches))');
+        '.then((caches) => Toaster.postMessage(caches))');
   }
 
-  Future<void> _onClearCache(
-      WebViewController controller, BuildContext context) async {
-    await controller.clearCache();
+  Future<void> _onClearCache(BuildContext context) async {
+    await webViewController.clearCache();
+    await webViewController.clearLocalStorage();
     ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
       content: Text('Cache cleared.'),
     ));
   }
 
-  Future<void> _onClearCookies(
-      WebViewController controller, BuildContext context) async {
-    final bool hadCookies = await WebViewCookieManager.instance.clearCookies();
+  Future<void> _onClearCookies(BuildContext context) async {
+    final bool hadCookies = await cookieManager.clearCookies();
     String message = 'There were cookies. Now, they are gone!';
     if (!hadCookies) {
       message = 'There are no cookies.';
@@ -366,47 +366,58 @@
     ));
   }
 
-  Future<void> _onSetCookie(
-      WebViewController controller, BuildContext context) async {
-    await WebViewCookieManager.instance.setCookie(
+  Future<void> _onNavigationDelegateExample() {
+    final String contentBase64 = base64Encode(
+      const Utf8Encoder().convert(kNavigationExamplePage),
+    );
+    return webViewController.loadRequest(
+      LoadRequestParams(
+        uri: Uri.parse('data:text/html;base64,$contentBase64'),
+      ),
+    );
+  }
+
+  Future<void> _onSetCookie() async {
+    await cookieManager.setCookie(
       const WebViewCookie(
-          name: 'foo', value: 'bar', domain: 'httpbin.org', path: '/anything'),
+        name: 'foo',
+        value: 'bar',
+        domain: 'httpbin.org',
+        path: '/anything',
+      ),
     );
-    await controller.loadUrl('https://httpbin.org/anything');
+    await webViewController.loadRequest(LoadRequestParams(
+      uri: Uri.parse('https://httpbin.org/anything'),
+    ));
   }
 
-  Future<void> _onNavigationDelegateExample(
-      WebViewController controller, BuildContext context) async {
-    final String contentBase64 =
-        base64Encode(const Utf8Encoder().convert(kNavigationExamplePage));
-    await controller.loadUrl('data:text/html;base64,$contentBase64');
-  }
-
-  Future<void> _onLoadFlutterAssetExample(
-      WebViewController controller, BuildContext context) async {
-    await controller.loadFlutterAsset('assets/www/index.html');
-  }
-
-  Future<void> _onLoadLocalFileExample(
-      WebViewController controller, BuildContext context) async {
-    final String pathToIndex = await _prepareLocalFile();
-
-    await controller.loadFile(pathToIndex);
-  }
-
-  Future<void> _onLoadHtmlStringExample(
-      WebViewController controller, BuildContext context) async {
-    await controller.loadHtmlString(kExamplePage);
-  }
-
-  Future<void> _onDoPostRequest(
-      WebViewController controller, BuildContext context) async {
-    final WebViewRequest request = WebViewRequest(
+  Future<void> _onDoPostRequest() {
+    return webViewController.loadRequest(LoadRequestParams(
       uri: Uri.parse('https://httpbin.org/post'),
-      method: WebViewRequestMethod.post,
+      method: LoadRequestMethod.post,
+      headers: const <String, String>{
+        'foo': 'bar',
+        'Content-Type': 'text/plain',
+      },
       body: Uint8List.fromList('Test Body'.codeUnits),
-    );
-    await controller.loadRequest(request);
+    ));
+  }
+
+  Future<void> _onLoadLocalFileExample() async {
+    final String pathToIndex = await _prepareLocalFile();
+    await webViewController.loadFile(pathToIndex);
+  }
+
+  Future<void> _onLoadFlutterAssetExample() {
+    return webViewController.loadFlutterAsset('assets/www/index.html');
+  }
+
+  Future<void> _onLoadHtmlStringExample() {
+    return webViewController.loadHtmlString(kLocalExamplePage);
+  }
+
+  Future<void> _onTransparentBackground() {
+    return webViewController.loadHtmlString(kTransparentBackgroundPage);
   }
 
   Widget _getCookieList(String cookies) {
@@ -425,83 +436,57 @@
 
   static Future<String> _prepareLocalFile() async {
     final String tmpDir = (await getTemporaryDirectory()).path;
-    final File indexFile = File('$tmpDir/www/index.html');
+    final File indexFile = File(
+        <String>{tmpDir, 'www', 'index.html'}.join(Platform.pathSeparator));
 
-    await Directory('$tmpDir/www').create(recursive: true);
-    await indexFile.writeAsString(kExamplePage);
+    await indexFile.create(recursive: true);
+    await indexFile.writeAsString(kLocalExamplePage);
 
     return indexFile.path;
   }
-
-  Future<void> _onTransparentBackground(
-      WebViewController controller, BuildContext context) async {
-    await controller.loadHtmlString(kTransparentBackgroundPage);
-  }
 }
 
-class _NavigationControls extends StatelessWidget {
-  const _NavigationControls(this._webViewControllerFuture)
-      : assert(_webViewControllerFuture != null);
+class NavigationControls extends StatelessWidget {
+  const NavigationControls({Key? key, required this.webViewController})
+      : super(key: key);
 
-  final Future<WebViewController> _webViewControllerFuture;
+  final PlatformWebViewController webViewController;
 
   @override
   Widget build(BuildContext context) {
-    return FutureBuilder<WebViewController>(
-      future: _webViewControllerFuture,
-      builder:
-          (BuildContext context, AsyncSnapshot<WebViewController> snapshot) {
-        final bool webViewReady =
-            snapshot.connectionState == ConnectionState.done;
-        final WebViewController? controller = snapshot.data;
-
-        return Row(
-          children: <Widget>[
-            IconButton(
-              icon: const Icon(Icons.arrow_back_ios),
-              onPressed: !webViewReady
-                  ? null
-                  : () async {
-                      if (await controller!.canGoBack()) {
-                        await controller.goBack();
-                      } else {
-                        ScaffoldMessenger.of(context).showSnackBar(
-                          const SnackBar(content: Text('No back history item')),
-                        );
-                        return;
-                      }
-                    },
-            ),
-            IconButton(
-              icon: const Icon(Icons.arrow_forward_ios),
-              onPressed: !webViewReady
-                  ? null
-                  : () async {
-                      if (await controller!.canGoForward()) {
-                        await controller.goForward();
-                      } else {
-                        ScaffoldMessenger.of(context).showSnackBar(
-                          const SnackBar(
-                              content: Text('No forward history item')),
-                        );
-                        return;
-                      }
-                    },
-            ),
-            IconButton(
-              icon: const Icon(Icons.replay),
-              onPressed: !webViewReady
-                  ? null
-                  : () {
-                      controller!.reload();
-                    },
-            ),
-          ],
-        );
-      },
+    return Row(
+      children: <Widget>[
+        IconButton(
+          icon: const Icon(Icons.arrow_back_ios),
+          onPressed: () async {
+            if (await webViewController.canGoBack()) {
+              await webViewController.goBack();
+            } else {
+              ScaffoldMessenger.of(context).showSnackBar(
+                const SnackBar(content: Text('No back history item')),
+              );
+              return;
+            }
+          },
+        ),
+        IconButton(
+          icon: const Icon(Icons.arrow_forward_ios),
+          onPressed: () async {
+            if (await webViewController.canGoForward()) {
+              await webViewController.goForward();
+            } else {
+              ScaffoldMessenger.of(context).showSnackBar(
+                const SnackBar(content: Text('No forward history item')),
+              );
+              return;
+            }
+          },
+        ),
+        IconButton(
+          icon: const Icon(Icons.replay),
+          onPressed: () => webViewController.reload(),
+        ),
+      ],
     );
   }
 }
-
-/// Callback type for handling messages sent from JavaScript running in a web view.
-typedef JavascriptMessageHandler = void Function(JavascriptMessage message);
diff --git a/packages/webview_flutter/webview_flutter_android/example/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/example/pubspec.yaml
index de6980a..0daacb0 100644
--- a/packages/webview_flutter/webview_flutter_android/example/pubspec.yaml
+++ b/packages/webview_flutter/webview_flutter_android/example/pubspec.yaml
@@ -18,7 +18,7 @@
     # The example app is bundled with the plugin so we use a path dependency on
     # the parent directory to use the current plugin's version.
     path: ../
-  webview_flutter_platform_interface: ^1.8.0
+  webview_flutter_platform_interface: ^2.0.0
 
 dev_dependencies:
   espresso: ^0.2.0
diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_navigation_delegate.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_navigation_delegate.dart
new file mode 100644
index 0000000..51c6276
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_navigation_delegate.dart
@@ -0,0 +1,318 @@
+// 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.
+
+import 'dart:async';
+
+import 'package:flutter/foundation.dart';
+import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
+
+import 'android_proxy.dart';
+import 'android_webview.dart' as android_webview;
+
+/// Signature for the `loadRequest` callback responsible for loading the [url]
+/// after a navigation request has been approved.
+typedef LoadRequestCallback = Future<void> Function(LoadRequestParams params);
+
+/// Error returned in `WebView.onWebResourceError` when a web resource loading error has occurred.
+@immutable
+class AndroidWebResourceError extends WebResourceError {
+  /// Creates a new [AndroidWebResourceError].
+  AndroidWebResourceError._({
+    required super.errorCode,
+    required super.description,
+    super.isForMainFrame,
+    this.failingUrl,
+  }) : super(
+          errorType: _errorCodeToErrorType(errorCode),
+        );
+
+  /// Gets the URL for which the failing resource request was made.
+  final String? failingUrl;
+
+  static WebResourceErrorType? _errorCodeToErrorType(int errorCode) {
+    switch (errorCode) {
+      case android_webview.WebViewClient.errorAuthentication:
+        return WebResourceErrorType.authentication;
+      case android_webview.WebViewClient.errorBadUrl:
+        return WebResourceErrorType.badUrl;
+      case android_webview.WebViewClient.errorConnect:
+        return WebResourceErrorType.connect;
+      case android_webview.WebViewClient.errorFailedSslHandshake:
+        return WebResourceErrorType.failedSslHandshake;
+      case android_webview.WebViewClient.errorFile:
+        return WebResourceErrorType.file;
+      case android_webview.WebViewClient.errorFileNotFound:
+        return WebResourceErrorType.fileNotFound;
+      case android_webview.WebViewClient.errorHostLookup:
+        return WebResourceErrorType.hostLookup;
+      case android_webview.WebViewClient.errorIO:
+        return WebResourceErrorType.io;
+      case android_webview.WebViewClient.errorProxyAuthentication:
+        return WebResourceErrorType.proxyAuthentication;
+      case android_webview.WebViewClient.errorRedirectLoop:
+        return WebResourceErrorType.redirectLoop;
+      case android_webview.WebViewClient.errorTimeout:
+        return WebResourceErrorType.timeout;
+      case android_webview.WebViewClient.errorTooManyRequests:
+        return WebResourceErrorType.tooManyRequests;
+      case android_webview.WebViewClient.errorUnknown:
+        return WebResourceErrorType.unknown;
+      case android_webview.WebViewClient.errorUnsafeResource:
+        return WebResourceErrorType.unsafeResource;
+      case android_webview.WebViewClient.errorUnsupportedAuthScheme:
+        return WebResourceErrorType.unsupportedAuthScheme;
+      case android_webview.WebViewClient.errorUnsupportedScheme:
+        return WebResourceErrorType.unsupportedScheme;
+    }
+
+    throw ArgumentError(
+      'Could not find a WebResourceErrorType for errorCode: $errorCode',
+    );
+  }
+}
+
+/// Object specifying creation parameters for creating a [AndroidNavigationDelegate].
+///
+/// When adding additional fields make sure they can be null or have a default
+/// value to avoid breaking changes. See [PlatformNavigationDelegateCreationParams] for
+/// more information.
+@immutable
+class AndroidNavigationDelegateCreationParams
+    extends PlatformNavigationDelegateCreationParams {
+  /// Creates a new [AndroidNavigationDelegateCreationParams] instance.
+  const AndroidNavigationDelegateCreationParams._({
+    @visibleForTesting this.androidWebViewProxy = const AndroidWebViewProxy(),
+  }) : super();
+
+  /// Creates a [AndroidNavigationDelegateCreationParams] instance based on [PlatformNavigationDelegateCreationParams].
+  factory AndroidNavigationDelegateCreationParams.fromPlatformNavigationDelegateCreationParams(
+    // Recommended placeholder to prevent being broken by platform interface.
+    // ignore: avoid_unused_constructor_parameters
+    PlatformNavigationDelegateCreationParams params, {
+    @visibleForTesting
+        AndroidWebViewProxy androidWebViewProxy = const AndroidWebViewProxy(),
+  }) {
+    return AndroidNavigationDelegateCreationParams._(
+      androidWebViewProxy: androidWebViewProxy,
+    );
+  }
+
+  /// Handles constructing objects and calling static methods for the Android WebView
+  /// native library.
+  @visibleForTesting
+  final AndroidWebViewProxy androidWebViewProxy;
+}
+
+/// A place to register callback methods responsible to handle navigation events
+/// triggered by the [android_webview.WebView].
+class AndroidNavigationDelegate extends PlatformNavigationDelegate {
+  /// Creates a new [AndroidNavigationkDelegate].
+  AndroidNavigationDelegate(PlatformNavigationDelegateCreationParams params)
+      : super.implementation(params is AndroidNavigationDelegateCreationParams
+            ? params
+            : AndroidNavigationDelegateCreationParams
+                .fromPlatformNavigationDelegateCreationParams(params)) {
+    final WeakReference<AndroidNavigationDelegate> weakThis =
+        WeakReference<AndroidNavigationDelegate>(this);
+
+    _webChromeClient = (this.params as AndroidNavigationDelegateCreationParams)
+        .androidWebViewProxy
+        .createAndroidWebChromeClient(
+            onProgressChanged: (android_webview.WebView webView, int progress) {
+      if (weakThis.target?._onProgress != null) {
+        weakThis.target!._onProgress!(progress);
+      }
+    });
+
+    _webViewClient = (this.params as AndroidNavigationDelegateCreationParams)
+        .androidWebViewProxy
+        .createAndroidWebViewClient(
+      onPageFinished: (android_webview.WebView webView, String url) {
+        if (weakThis.target?._onPageFinished != null) {
+          weakThis.target!._onPageFinished!(url);
+        }
+      },
+      onPageStarted: (android_webview.WebView webView, String url) {
+        if (weakThis.target?._onPageStarted != null) {
+          weakThis.target!._onPageStarted!(url);
+        }
+      },
+      onReceivedRequestError: (
+        android_webview.WebView webView,
+        android_webview.WebResourceRequest request,
+        android_webview.WebResourceError error,
+      ) {
+        if (weakThis.target?._onWebResourceError != null) {
+          weakThis.target!._onWebResourceError!(AndroidWebResourceError._(
+            errorCode: error.errorCode,
+            description: error.description,
+            failingUrl: request.url,
+            isForMainFrame: request.isForMainFrame,
+          ));
+        }
+      },
+      onReceivedError: (
+        android_webview.WebView webView,
+        int errorCode,
+        String description,
+        String failingUrl,
+      ) {
+        if (weakThis.target?._onWebResourceError != null) {
+          weakThis.target!._onWebResourceError!(AndroidWebResourceError._(
+            errorCode: errorCode,
+            description: description,
+            failingUrl: failingUrl,
+            isForMainFrame: true,
+          ));
+        }
+      },
+      requestLoading: (
+        android_webview.WebView webView,
+        android_webview.WebResourceRequest request,
+      ) {
+        if (weakThis.target != null) {
+          weakThis.target!._handleNavigation(
+            request.url,
+            headers: request.requestHeaders,
+            isForMainFrame: request.isForMainFrame,
+          );
+        }
+      },
+      urlLoading: (
+        android_webview.WebView webView,
+        String url,
+      ) {
+        if (weakThis.target != null) {
+          weakThis.target!._handleNavigation(url, isForMainFrame: true);
+        }
+      },
+    );
+
+    _downloadListener = (this.params as AndroidNavigationDelegateCreationParams)
+        .androidWebViewProxy
+        .createDownloadListener(
+      onDownloadStart: (
+        String url,
+        String userAgent,
+        String contentDisposition,
+        String mimetype,
+        int contentLength,
+      ) {
+        if (weakThis.target != null) {
+          weakThis.target?._handleNavigation(url, isForMainFrame: true);
+        }
+      },
+    );
+  }
+
+  late final android_webview.WebChromeClient _webChromeClient;
+
+  /// Gets the native [android_webview.WebChromeClient] that is bridged by this [AndroidNavigationDelegate].
+  ///
+  /// Used by the [AndroidWebViewController] to set the `android_webview.WebView.setWebChromeClient`.
+  android_webview.WebChromeClient get androidWebChromeClient =>
+      _webChromeClient;
+
+  late final android_webview.WebViewClient _webViewClient;
+
+  /// Gets the native [android_webview.WebViewClient] that is bridged by this [AndroidNavigationDelegate].
+  ///
+  /// Used by the [AndroidWebViewController] to set the `android_webview.WebView.setWebViewClient`.
+  android_webview.WebViewClient get androidWebViewClient => _webViewClient;
+
+  late final android_webview.DownloadListener _downloadListener;
+
+  /// Gets the native [android_webview.DownloadListener] that is bridged by this [AndroidNavigationDelegate].
+  ///
+  /// Used by the [AndroidWebViewController] to set the `android_webview.WebView.setDownloadListener`.
+  android_webview.DownloadListener get androidDownloadListener =>
+      _downloadListener;
+
+  PageEventCallback? _onPageFinished;
+  PageEventCallback? _onPageStarted;
+  ProgressCallback? _onProgress;
+  WebResourceErrorCallback? _onWebResourceError;
+  NavigationRequestCallback? _onNavigationRequest;
+  LoadRequestCallback? _onLoadRequest;
+
+  void _handleNavigation(
+    String url, {
+    required bool isForMainFrame,
+    Map<String, String> headers = const <String, String>{},
+  }) {
+    final LoadRequestCallback? onLoadRequest = _onLoadRequest;
+    final NavigationRequestCallback? onNavigationRequest = _onNavigationRequest;
+
+    if (onNavigationRequest == null || onLoadRequest == null) {
+      return;
+    }
+
+    final FutureOr<NavigationDecision> returnValue = onNavigationRequest(
+      NavigationRequest(
+        url: url,
+        isMainFrame: isForMainFrame,
+      ),
+    );
+
+    if (returnValue is NavigationDecision &&
+        returnValue == NavigationDecision.navigate) {
+      onLoadRequest(LoadRequestParams(
+        uri: Uri.parse(url),
+        headers: headers,
+      ));
+    } else if (returnValue is Future<NavigationDecision>) {
+      returnValue.then((NavigationDecision shouldLoadUrl) {
+        if (shouldLoadUrl == NavigationDecision.navigate) {
+          onLoadRequest(LoadRequestParams(
+            uri: Uri.parse(url),
+            headers: headers,
+          ));
+        }
+      });
+    }
+  }
+
+  /// Invoked when loading the url after a navigation request is approved.
+  Future<void> setOnLoadRequest(
+    LoadRequestCallback onLoadRequest,
+  ) async {
+    _onLoadRequest = onLoadRequest;
+  }
+
+  @override
+  Future<void> setOnNavigationRequest(
+    NavigationRequestCallback onNavigationRequest,
+  ) async {
+    _onNavigationRequest = onNavigationRequest;
+    _webViewClient.setSynchronousReturnValueForShouldOverrideUrlLoading(true);
+  }
+
+  @override
+  Future<void> setOnPageStarted(
+    PageEventCallback onPageStarted,
+  ) async {
+    _onPageStarted = onPageStarted;
+  }
+
+  @override
+  Future<void> setOnPageFinished(
+    PageEventCallback onPageFinished,
+  ) async {
+    _onPageFinished = onPageFinished;
+  }
+
+  @override
+  Future<void> setOnProgress(
+    ProgressCallback onProgress,
+  ) async {
+    _onProgress = onProgress;
+  }
+
+  @override
+  Future<void> setOnWebResourceError(
+    WebResourceErrorCallback onWebResourceError,
+  ) async {
+    _onWebResourceError = onWebResourceError;
+  }
+}
diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_proxy.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_proxy.dart
new file mode 100644
index 0000000..db247ee
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_proxy.dart
@@ -0,0 +1,101 @@
+// 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.
+
+import 'android_webview.dart' as android_webview;
+
+/// Handles constructing objects and calling static methods for the Android
+/// WebView native library.
+///
+/// This class provides dependency injection for the implementations of the
+/// platform interface classes. Improving the ease of unit testing and/or
+/// overriding the underlying Android WebView classes.
+///
+/// By default each function calls the default constructor of the WebView class
+/// it intends to return.
+class AndroidWebViewProxy {
+  /// Constructs a [AndroidWebViewProxy].
+  const AndroidWebViewProxy({
+    this.createAndroidWebView = android_webview.WebView.new,
+    this.createAndroidWebChromeClient = android_webview.WebChromeClient.new,
+    this.createAndroidWebViewClient = android_webview.WebViewClient.new,
+    this.createFlutterAssetManager = android_webview.FlutterAssetManager.new,
+    this.createJavaScriptChannel = android_webview.JavaScriptChannel.new,
+    this.createDownloadListener = android_webview.DownloadListener.new,
+  });
+
+  /// Constructs a [android_webview.WebView].
+  ///
+  /// Due to changes in Flutter 3.0 the [useHybridComposition] doesn't have
+  /// any effect and should not be exposed publicly. More info here:
+  /// https://github.com/flutter/flutter/issues/108106
+  final android_webview.WebView Function({
+    required bool useHybridComposition,
+  }) createAndroidWebView;
+
+  /// Constructs a [android_webview.WebChromeClient].
+  final android_webview.WebChromeClient Function({
+    void Function(android_webview.WebView webView, int progress)?
+        onProgressChanged,
+  }) createAndroidWebChromeClient;
+
+  /// Constructs a [android_webview.WebViewClient].
+  final android_webview.WebViewClient Function({
+    void Function(android_webview.WebView webView, String url)? onPageStarted,
+    void Function(android_webview.WebView webView, String url)? onPageFinished,
+    void Function(
+      android_webview.WebView webView,
+      android_webview.WebResourceRequest request,
+      android_webview.WebResourceError error,
+    )?
+        onReceivedRequestError,
+    @Deprecated('Only called on Android version < 23.')
+        void Function(
+      android_webview.WebView webView,
+      int errorCode,
+      String description,
+      String failingUrl,
+    )?
+            onReceivedError,
+    void Function(
+      android_webview.WebView webView,
+      android_webview.WebResourceRequest request,
+    )?
+        requestLoading,
+    void Function(android_webview.WebView webView, String url)? urlLoading,
+  }) createAndroidWebViewClient;
+
+  /// Constructs a [android_webview.FlutterAssetManager].
+  final android_webview.FlutterAssetManager Function()
+      createFlutterAssetManager;
+
+  /// Constructs a [android_webview.JavaScriptChannel].
+  final android_webview.JavaScriptChannel Function(
+    String channelName, {
+    required void Function(String) postMessage,
+  }) createJavaScriptChannel;
+
+  /// Constructs a [android_webview.DownloadListener].
+  final android_webview.DownloadListener Function({
+    required void Function(
+      String url,
+      String userAgent,
+      String contentDisposition,
+      String mimetype,
+      int contentLength,
+    )
+        onDownloadStart,
+  }) createDownloadListener;
+
+  /// Enables debugging of web contents (HTML / CSS / JavaScript) loaded into any WebViews of this application.
+  ///
+  /// This flag can be enabled in order to facilitate debugging of web layouts
+  /// and JavaScript code running inside WebViews. Please refer to
+  /// [android_webview.WebView] documentation for the debugging guide. The
+  /// default is false.
+  ///
+  /// See [android_webview.WebView].setWebContentsDebuggingEnabled.
+  Future<void> setWebContentsDebuggingEnabled(bool enabled) {
+    return android_webview.WebView.setWebContentsDebuggingEnabled(enabled);
+  }
+}
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..66f93dd 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].
@@ -76,6 +78,10 @@
 /// When a [WebView] is no longer needed [release] must be called.
 class WebView extends JavaObject {
   /// Constructs a new WebView.
+  ///
+  /// Due to changes in Flutter 3.0 the [useHybridComposition] doesn't have
+  /// any effect and should not be exposed publicly. More info here:
+  /// https://github.com/flutter/flutter/issues/108106
   WebView({this.useHybridComposition = false}) : super.detached() {
     api.createFromInstance(this);
   }
@@ -90,8 +96,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 +334,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 +377,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 +386,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 +395,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 +602,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 +615,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 +626,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() {
@@ -659,26 +638,15 @@
 class WebViewClient extends JavaObject {
   /// 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.
@@ -686,24 +654,12 @@
   /// This should only be used by subclasses created by this library or to
   /// 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.
@@ -790,20 +746,6 @@
   @visibleForTesting
   static WebViewClientHostApiImpl api = WebViewClientHostApiImpl();
 
-  /// Whether loading a url should be overridden.
-  ///
-  /// In Java, `shouldOverrideUrlLoading()` and `shouldOverrideRequestLoading()`
-  /// callbacks must synchronously return a boolean. This sets the default
-  /// return value.
-  ///
-  /// 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. [requestLoading] or [urlLoading] will
-  /// still be called either way.
-  ///
-  /// Defaults to true.
-  final bool shouldOverrideUrlLoading;
-
   /// Notify the host application that a page has started loading.
   ///
   /// This method is called once for each main frame load so a page with iframes
@@ -812,7 +754,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 +762,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,48 +771,58 @@
   /// 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].
+  /// When the current [WebView] wants to load a URL.
   ///
-  /// If a [WebViewClient] is not provided, by default [WebView] will ask
-  /// Activity Manager to choose the proper handler for the URL. If a
-  /// [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) {}
+  /// The value set by [setSynchronousReturnValueForShouldOverrideUrlLoading]
+  /// indicates whether the [WebView] loaded the 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].
+  /// When the current [WebView] wants to load a URL.
   ///
-  /// If a [WebViewClient] is not provided, by default [WebView] will ask
-  /// Activity Manager to choose the proper handler for the URL. If a
-  /// [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) {}
+  /// The value set by [setSynchronousReturnValueForShouldOverrideUrlLoading]
+  /// indicates whether the [WebView] loaded the URL.
+  final void Function(WebView webView, String url)? urlLoading;
+
+  /// Sets the required synchronous return value for the Java method,
+  /// `WebViewClient.shouldOverrideUrlLoading(...)`.
+  ///
+  /// The Java method, `WebViewClient.shouldOverrideUrlLoading(...)`, requires
+  /// a boolean to be returned and this method sets the returned value for all
+  /// calls to the Java method.
+  ///
+  /// Setting this to true causes the current [WebView] to abort loading any URL
+  /// received by [requestLoading] or [urlLoading], while setting this to false
+  /// causes the [WebView] to continue loading a URL as usual.
+  ///
+  /// Defaults to false.
+  Future<void> setSynchronousReturnValueForShouldOverrideUrlLoading(
+    bool value,
+  ) {
+    return api.setShouldOverrideUrlLoadingReturnValueFromInstance(this, value);
+  }
 
   @override
   WebViewClient copy() {
     return WebViewClient.detached(
-      shouldOverrideUrlLoading: shouldOverrideUrlLoading,
       onPageStarted: onPageStarted,
       onPageFinished: onPageFinished,
       onReceivedRequestError: onReceivedRequestError,
@@ -885,17 +837,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 +847,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 +881,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..5bdab16 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
@@ -1,7 +1,7 @@
 // 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.
-// Autogenerated from Pigeon (v4.0.2), do not edit directly.
+// Autogenerated from Pigeon (v4.2.3), do not edit directly.
 // See also: https://pub.dev/packages/pigeon
 // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import
 import 'dart:async';
@@ -102,20 +102,20 @@
   }
 }
 
-class _JavaObjectHostApiCodec extends StandardMessageCodec {
-  const _JavaObjectHostApiCodec();
-}
-
+/// Handles methods calls to the native Java Object class.
+///
+/// Also handles calls to remove the reference to an instance with `dispose`.
+///
+/// See https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html.
 class JavaObjectHostApi {
   /// Constructor for [JavaObjectHostApi].  The [binaryMessenger] named argument is
   /// available for dependency injection.  If it is left null, the default
   /// BinaryMessenger will be used which routes to the host platform.
   JavaObjectHostApi({BinaryMessenger? binaryMessenger})
       : _binaryMessenger = binaryMessenger;
-
   final BinaryMessenger? _binaryMessenger;
 
-  static const MessageCodec<Object?> codec = _JavaObjectHostApiCodec();
+  static const MessageCodec<Object?> codec = StandardMessageCodec();
 
   Future<void> dispose(int arg_identifier) async {
     final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
@@ -142,12 +142,11 @@
   }
 }
 
-class _JavaObjectFlutterApiCodec extends StandardMessageCodec {
-  const _JavaObjectFlutterApiCodec();
-}
-
+/// Handles callbacks methods for the native Java Object class.
+///
+/// See https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html.
 abstract class JavaObjectFlutterApi {
-  static const MessageCodec<Object?> codec = _JavaObjectFlutterApiCodec();
+  static const MessageCodec<Object?> codec = StandardMessageCodec();
 
   void dispose(int identifier);
   static void setup(JavaObjectFlutterApi? api,
@@ -174,20 +173,15 @@
   }
 }
 
-class _CookieManagerHostApiCodec extends StandardMessageCodec {
-  const _CookieManagerHostApiCodec();
-}
-
 class CookieManagerHostApi {
   /// Constructor for [CookieManagerHostApi].  The [binaryMessenger] named argument is
   /// available for dependency injection.  If it is left null, the default
   /// BinaryMessenger will be used which routes to the host platform.
   CookieManagerHostApi({BinaryMessenger? binaryMessenger})
       : _binaryMessenger = binaryMessenger;
-
   final BinaryMessenger? _binaryMessenger;
 
-  static const MessageCodec<Object?> codec = _CookieManagerHostApiCodec();
+  static const MessageCodec<Object?> codec = StandardMessageCodec();
 
   Future<bool> clearCookies() async {
     final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
@@ -273,7 +267,6 @@
   /// BinaryMessenger will be used which routes to the host platform.
   WebViewHostApi({BinaryMessenger? binaryMessenger})
       : _binaryMessenger = binaryMessenger;
-
   final BinaryMessenger? _binaryMessenger;
 
   static const MessageCodec<Object?> codec = _WebViewHostApiCodec();
@@ -303,30 +296,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?>(
@@ -987,20 +956,15 @@
   }
 }
 
-class _WebSettingsHostApiCodec extends StandardMessageCodec {
-  const _WebSettingsHostApiCodec();
-}
-
 class WebSettingsHostApi {
   /// Constructor for [WebSettingsHostApi].  The [binaryMessenger] named argument is
   /// available for dependency injection.  If it is left null, the default
   /// BinaryMessenger will be used which routes to the host platform.
   WebSettingsHostApi({BinaryMessenger? binaryMessenger})
       : _binaryMessenger = binaryMessenger;
-
   final BinaryMessenger? _binaryMessenger;
 
-  static const MessageCodec<Object?> codec = _WebSettingsHostApiCodec();
+  static const MessageCodec<Object?> codec = StandardMessageCodec();
 
   Future<void> create(int arg_instanceId, int arg_webViewInstanceId) async {
     final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
@@ -1027,30 +991,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,
@@ -1352,20 +1292,15 @@
   }
 }
 
-class _JavaScriptChannelHostApiCodec extends StandardMessageCodec {
-  const _JavaScriptChannelHostApiCodec();
-}
-
 class JavaScriptChannelHostApi {
   /// Constructor for [JavaScriptChannelHostApi].  The [binaryMessenger] named argument is
   /// available for dependency injection.  If it is left null, the default
   /// BinaryMessenger will be used which routes to the host platform.
   JavaScriptChannelHostApi({BinaryMessenger? binaryMessenger})
       : _binaryMessenger = binaryMessenger;
-
   final BinaryMessenger? _binaryMessenger;
 
-  static const MessageCodec<Object?> codec = _JavaScriptChannelHostApiCodec();
+  static const MessageCodec<Object?> codec = StandardMessageCodec();
 
   Future<void> create(int arg_instanceId, String arg_channelName) async {
     final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
@@ -1393,39 +1328,14 @@
   }
 }
 
-class _JavaScriptChannelFlutterApiCodec extends StandardMessageCodec {
-  const _JavaScriptChannelFlutterApiCodec();
-}
-
 abstract class JavaScriptChannelFlutterApi {
-  static const MessageCodec<Object?> codec =
-      _JavaScriptChannelFlutterApiCodec();
+  static const MessageCodec<Object?> codec = StandardMessageCodec();
 
-  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) {
@@ -1449,29 +1359,48 @@
   }
 }
 
-class _WebViewClientHostApiCodec extends StandardMessageCodec {
-  const _WebViewClientHostApiCodec();
-}
-
 class WebViewClientHostApi {
   /// Constructor for [WebViewClientHostApi].  The [binaryMessenger] named argument is
   /// available for dependency injection.  If it is left null, the default
   /// BinaryMessenger will be used which routes to the host platform.
   WebViewClientHostApi({BinaryMessenger? binaryMessenger})
       : _binaryMessenger = binaryMessenger;
-
   final BinaryMessenger? _binaryMessenger;
 
-  static const MessageCodec<Object?> codec = _WebViewClientHostApiCodec();
+  static const MessageCodec<Object?> codec = StandardMessageCodec();
 
-  Future<void> create(
-      int arg_instanceId, bool arg_shouldOverrideUrlLoading) async {
+  Future<void> create(int arg_instanceId) async {
     final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
         'dev.flutter.pigeon.WebViewClientHostApi.create', 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> setSynchronousReturnValueForShouldOverrideUrlLoading(
+      int arg_instanceId, bool arg_value) async {
+    final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+        'dev.flutter.pigeon.WebViewClientHostApi.setSynchronousReturnValueForShouldOverrideUrlLoading',
+        codec,
+        binaryMessenger: _binaryMessenger);
     final Map<Object?, Object?>? replyMap = await channel
-            .send(<Object?>[arg_instanceId, arg_shouldOverrideUrlLoading])
-        as Map<Object?, Object?>?;
+        .send(<Object?>[arg_instanceId, arg_value]) as Map<Object?, Object?>?;
     if (replyMap == null) {
       throw PlatformException(
         code: 'channel-error',
@@ -1524,7 +1453,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 +1466,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) {
@@ -1724,20 +1633,15 @@
   }
 }
 
-class _DownloadListenerHostApiCodec extends StandardMessageCodec {
-  const _DownloadListenerHostApiCodec();
-}
-
 class DownloadListenerHostApi {
   /// Constructor for [DownloadListenerHostApi].  The [binaryMessenger] named argument is
   /// available for dependency injection.  If it is left null, the default
   /// BinaryMessenger will be used which routes to the host platform.
   DownloadListenerHostApi({BinaryMessenger? binaryMessenger})
       : _binaryMessenger = binaryMessenger;
-
   final BinaryMessenger? _binaryMessenger;
 
-  static const MessageCodec<Object?> codec = _DownloadListenerHostApiCodec();
+  static const MessageCodec<Object?> codec = StandardMessageCodec();
 
   Future<void> create(int arg_instanceId) async {
     final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
@@ -1764,39 +1668,15 @@
   }
 }
 
-class _DownloadListenerFlutterApiCodec extends StandardMessageCodec {
-  const _DownloadListenerFlutterApiCodec();
-}
-
 abstract class DownloadListenerFlutterApi {
-  static const MessageCodec<Object?> codec = _DownloadListenerFlutterApiCodec();
+  static const MessageCodec<Object?> codec = StandardMessageCodec();
 
-  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);
@@ -1834,29 +1714,22 @@
   }
 }
 
-class _WebChromeClientHostApiCodec extends StandardMessageCodec {
-  const _WebChromeClientHostApiCodec();
-}
-
 class WebChromeClientHostApi {
   /// Constructor for [WebChromeClientHostApi].  The [binaryMessenger] named argument is
   /// available for dependency injection.  If it is left null, the default
   /// BinaryMessenger will be used which routes to the host platform.
   WebChromeClientHostApi({BinaryMessenger? binaryMessenger})
       : _binaryMessenger = binaryMessenger;
-
   final BinaryMessenger? _binaryMessenger;
 
-  static const MessageCodec<Object?> codec = _WebChromeClientHostApiCodec();
+  static const MessageCodec<Object?> codec = StandardMessageCodec();
 
-  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',
@@ -1876,20 +1749,15 @@
   }
 }
 
-class _FlutterAssetManagerHostApiCodec extends StandardMessageCodec {
-  const _FlutterAssetManagerHostApiCodec();
-}
-
 class FlutterAssetManagerHostApi {
   /// Constructor for [FlutterAssetManagerHostApi].  The [binaryMessenger] named argument is
   /// available for dependency injection.  If it is left null, the default
   /// BinaryMessenger will be used which routes to the host platform.
   FlutterAssetManagerHostApi({BinaryMessenger? binaryMessenger})
       : _binaryMessenger = binaryMessenger;
-
   final BinaryMessenger? _binaryMessenger;
 
-  static const MessageCodec<Object?> codec = _FlutterAssetManagerHostApiCodec();
+  static const MessageCodec<Object?> codec = StandardMessageCodec();
 
   Future<List<String?>> list(String arg_path) async {
     final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
@@ -1951,38 +1819,14 @@
   }
 }
 
-class _WebChromeClientFlutterApiCodec extends StandardMessageCodec {
-  const _WebChromeClientFlutterApiCodec();
-}
-
 abstract class WebChromeClientFlutterApi {
-  static const MessageCodec<Object?> codec = _WebChromeClientFlutterApiCodec();
+  static const MessageCodec<Object?> codec = StandardMessageCodec();
 
-  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);
@@ -2011,20 +1855,15 @@
   }
 }
 
-class _WebStorageHostApiCodec extends StandardMessageCodec {
-  const _WebStorageHostApiCodec();
-}
-
 class WebStorageHostApi {
   /// Constructor for [WebStorageHostApi].  The [binaryMessenger] named argument is
   /// available for dependency injection.  If it is left null, the default
   /// BinaryMessenger will be used which routes to the host platform.
   WebStorageHostApi({BinaryMessenger? binaryMessenger})
       : _binaryMessenger = binaryMessenger;
-
   final BinaryMessenger? _binaryMessenger;
 
-  static const MessageCodec<Object?> codec = _WebStorageHostApiCodec();
+  static const MessageCodec<Object?> codec = StandardMessageCodec();
 
   Future<void> create(int arg_instanceId) async {
     final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
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..8aa5f7d 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
@@ -128,10 +128,9 @@
 class WebViewHostApiImpl extends WebViewHostApi {
   /// Constructs a [WebViewHostApiImpl].
   WebViewHostApiImpl({
-    BinaryMessenger? binaryMessenger,
+    super.binaryMessenger,
     InstanceManager? instanceManager,
-  })  : instanceManager = instanceManager ?? JavaObject.globalInstanceManager,
-        super(binaryMessenger: binaryMessenger);
+  }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager;
 
   /// Maintains instances stored to communicate with java objects.
   final InstanceManager instanceManager;
@@ -144,15 +143,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,
@@ -351,10 +341,9 @@
 class WebSettingsHostApiImpl extends WebSettingsHostApi {
   /// Constructs a [WebSettingsHostApiImpl].
   WebSettingsHostApiImpl({
-    BinaryMessenger? binaryMessenger,
+    super.binaryMessenger,
     InstanceManager? instanceManager,
-  })  : instanceManager = instanceManager ?? JavaObject.globalInstanceManager,
-        super(binaryMessenger: binaryMessenger);
+  }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager;
 
   /// Maintains instances stored to communicate with java objects.
   final InstanceManager instanceManager;
@@ -368,15 +357,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,
@@ -502,10 +482,9 @@
 class JavaScriptChannelHostApiImpl extends JavaScriptChannelHostApi {
   /// Constructs a [JavaScriptChannelHostApiImpl].
   JavaScriptChannelHostApiImpl({
-    BinaryMessenger? binaryMessenger,
+    super.binaryMessenger,
     InstanceManager? instanceManager,
-  })  : instanceManager = instanceManager ?? JavaObject.globalInstanceManager,
-        super(binaryMessenger: binaryMessenger);
+  }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager;
 
   /// Maintains instances stored to communicate with java objects.
   final InstanceManager instanceManager;
@@ -532,11 +511,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?;
@@ -552,10 +526,9 @@
 class WebViewClientHostApiImpl extends WebViewClientHostApi {
   /// Constructs a [WebViewClientHostApiImpl].
   WebViewClientHostApiImpl({
-    BinaryMessenger? binaryMessenger,
+    super.binaryMessenger,
     InstanceManager? instanceManager,
-  })  : instanceManager = instanceManager ?? JavaObject.globalInstanceManager,
-        super(binaryMessenger: binaryMessenger);
+  }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager;
 
   /// Maintains instances stored to communicate with java objects.
   final InstanceManager instanceManager;
@@ -564,9 +537,20 @@
   Future<void> createFromInstance(WebViewClient instance) async {
     if (instanceManager.getIdentifier(instance) == null) {
       final int identifier = instanceManager.addDartCreatedInstance(instance);
-      return create(identifier, instance.shouldOverrideUrlLoading);
+      return create(identifier);
     }
   }
+
+  /// Helper method to convert instances ids to objects.
+  Future<void> setShouldOverrideUrlLoadingReturnValueFromInstance(
+    WebViewClient instance,
+    bool value,
+  ) {
+    return setSynchronousReturnValueForShouldOverrideUrlLoading(
+      instanceManager.getIdentifier(instance)!,
+      value,
+    );
+  }
 }
 
 /// Flutter api implementation for [WebViewClient].
@@ -579,11 +563,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 +576,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 +595,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 +621,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 +650,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 +677,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 +703,9 @@
       webViewInstance != null,
       'InstanceManager does not contain an WebView with instanceId: $webViewInstanceId',
     );
-    instance!.urlLoading(webViewInstance!, url);
+    if (instance!.urlLoading != null) {
+      instance.urlLoading!(webViewInstance!, url);
+    }
   }
 }
 
@@ -719,10 +713,9 @@
 class DownloadListenerHostApiImpl extends DownloadListenerHostApi {
   /// Constructs a [DownloadListenerHostApiImpl].
   DownloadListenerHostApiImpl({
-    BinaryMessenger? binaryMessenger,
+    super.binaryMessenger,
     InstanceManager? instanceManager,
-  })  : instanceManager = instanceManager ?? JavaObject.globalInstanceManager,
-        super(binaryMessenger: binaryMessenger);
+  }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager;
 
   /// Maintains instances stored to communicate with java objects.
   final InstanceManager instanceManager;
@@ -746,11 +739,6 @@
   final InstanceManager instanceManager;
 
   @override
-  void dispose(int instanceId) {
-    instanceManager.remove(instanceId);
-  }
-
-  @override
   void onDownloadStart(
     int instanceId,
     String url,
@@ -779,25 +767,18 @@
 class WebChromeClientHostApiImpl extends WebChromeClientHostApi {
   /// Constructs a [WebChromeClientHostApiImpl].
   WebChromeClientHostApiImpl({
-    BinaryMessenger? binaryMessenger,
+    super.binaryMessenger,
     InstanceManager? instanceManager,
-  })  : instanceManager = instanceManager ?? JavaObject.globalInstanceManager,
-        super(binaryMessenger: binaryMessenger);
+  }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager;
 
   /// Maintains instances stored to communicate with java objects.
   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 +793,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 +806,9 @@
       webViewInstance != null,
       'InstanceManager does not contain an WebView with instanceId: $webViewInstanceId',
     );
-    instance!.onProgressChanged(webViewInstance!, progress);
+    if (instance!.onProgressChanged != null) {
+      instance.onProgressChanged!(webViewInstance!, progress);
+    }
   }
 }
 
@@ -838,10 +816,9 @@
 class WebStorageHostApiImpl extends WebStorageHostApi {
   /// Constructs a [WebStorageHostApiImpl].
   WebStorageHostApiImpl({
-    BinaryMessenger? binaryMessenger,
+    super.binaryMessenger,
     InstanceManager? instanceManager,
-  })  : instanceManager = instanceManager ?? JavaObject.globalInstanceManager,
-        super(binaryMessenger: binaryMessenger);
+  }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager;
 
   /// Maintains instances stored to communicate with java objects.
   final InstanceManager instanceManager;
diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_controller.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_controller.dart
new file mode 100644
index 0000000..2cb01d4
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_controller.dart
@@ -0,0 +1,440 @@
+// 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.
+
+// TODO(a14n): remove this import once Flutter 3.1 or later reaches stable (including flutter/flutter#104231)
+// ignore: unnecessary_import
+import 'dart:typed_data';
+
+import 'package:flutter/material.dart';
+import 'package:flutter/rendering.dart';
+import 'package:flutter/services.dart';
+import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
+
+import 'android_navigation_delegate.dart';
+import 'android_proxy.dart';
+import 'android_webview.dart' as android_webview;
+import 'android_webview.dart';
+import 'instance_manager.dart';
+import 'weak_reference_utils.dart';
+
+/// Object specifying creation parameters for creating a [AndroidWebViewController].
+///
+/// When adding additional fields make sure they can be null or have a default
+/// value to avoid breaking changes. See [PlatformWebViewControllerCreationParams] for
+/// more information.
+@immutable
+class AndroidWebViewControllerCreationParams
+    extends PlatformWebViewControllerCreationParams {
+  /// Creates a new [AndroidWebViewControllerCreationParams] instance.
+  AndroidWebViewControllerCreationParams({
+    @visibleForTesting this.androidWebViewProxy = const AndroidWebViewProxy(),
+    @visibleForTesting android_webview.WebStorage? androidWebStorage,
+  })  : androidWebStorage =
+            androidWebStorage ?? android_webview.WebStorage.instance,
+        super();
+
+  /// Creates a [AndroidWebViewControllerCreationParams] instance based on [PlatformWebViewControllerCreationParams].
+  factory AndroidWebViewControllerCreationParams.fromPlatformWebViewControllerCreationParams(
+    // Recommended placeholder to prevent being broken by platform interface.
+    // ignore: avoid_unused_constructor_parameters
+    PlatformWebViewControllerCreationParams params, {
+    @visibleForTesting
+        AndroidWebViewProxy androidWebViewProxy = const AndroidWebViewProxy(),
+    @visibleForTesting android_webview.WebStorage? androidWebStorage,
+  }) {
+    return AndroidWebViewControllerCreationParams(
+      androidWebViewProxy: androidWebViewProxy,
+      androidWebStorage:
+          androidWebStorage ?? android_webview.WebStorage.instance,
+    );
+  }
+
+  /// Handles constructing objects and calling static methods for the Android WebView
+  /// native library.
+  @visibleForTesting
+  final AndroidWebViewProxy androidWebViewProxy;
+
+  /// Manages the JavaScript storage APIs provided by the [android_webview.WebView].
+  @visibleForTesting
+  final android_webview.WebStorage androidWebStorage;
+}
+
+/// Implementation of the [PlatformWebViewController] with the Android WebView API.
+class AndroidWebViewController extends PlatformWebViewController {
+  /// Creates a new [AndroidWebViewCookieManager].
+  AndroidWebViewController(PlatformWebViewControllerCreationParams params)
+      : super.implementation(params is AndroidWebViewControllerCreationParams
+            ? params
+            : AndroidWebViewControllerCreationParams
+                .fromPlatformWebViewControllerCreationParams(params)) {
+    _webView.settings.setDomStorageEnabled(true);
+    _webView.settings.setJavaScriptCanOpenWindowsAutomatically(true);
+    _webView.settings.setSupportMultipleWindows(true);
+    _webView.settings.setLoadWithOverviewMode(true);
+    _webView.settings.setUseWideViewPort(true);
+    _webView.settings.setDisplayZoomControls(false);
+    _webView.settings.setBuiltInZoomControls(true);
+  }
+
+  AndroidWebViewControllerCreationParams get _androidWebViewParams =>
+      params as AndroidWebViewControllerCreationParams;
+
+  /// The native [android_webview.WebView] being controlled.
+  late final android_webview.WebView _webView =
+      _androidWebViewParams.androidWebViewProxy.createAndroidWebView(
+    // Due to changes in Flutter 3.0 the `useHybridComposition` doesn't have
+    // any effect and is purposefully not exposed publicly by the
+    // [AndroidWebViewController]. More info here:
+    // https://github.com/flutter/flutter/issues/108106
+    useHybridComposition: true,
+  );
+
+  /// The native [android_webview.FlutterAssetManager] allows managing assets.
+  late final android_webview.FlutterAssetManager _flutterAssetManager =
+      _androidWebViewParams.androidWebViewProxy.createFlutterAssetManager();
+
+  final Map<String, AndroidJavaScriptChannelParams> _javaScriptChannelParams =
+      <String, AndroidJavaScriptChannelParams>{};
+
+  // The keeps a reference to the current NavigationDelegate so that the
+  // callback methods remain reachable.
+  // ignore: unused_field
+  late AndroidNavigationDelegate _currentNavigationDelegate;
+
+  /// Whether to enable the platform's webview content debugging tools.
+  ///
+  /// Defaults to false.
+  static Future<void> enableDebugging(
+    bool enabled, {
+    @visibleForTesting
+        AndroidWebViewProxy webViewProxy = const AndroidWebViewProxy(),
+  }) {
+    return webViewProxy.setWebContentsDebuggingEnabled(enabled);
+  }
+
+  @override
+  Future<void> loadFile(
+    String absoluteFilePath,
+  ) {
+    final String url = absoluteFilePath.startsWith('file://')
+        ? absoluteFilePath
+        : Uri.file(absoluteFilePath).toString();
+
+    _webView.settings.setAllowFileAccess(true);
+    return _webView.loadUrl(url, <String, String>{});
+  }
+
+  @override
+  Future<void> loadFlutterAsset(
+    String key,
+  ) async {
+    final String assetFilePath =
+        await _flutterAssetManager.getAssetFilePathByName(key);
+    final List<String> pathElements = assetFilePath.split('/');
+    final String fileName = pathElements.removeLast();
+    final List<String?> paths =
+        await _flutterAssetManager.list(pathElements.join('/'));
+
+    if (!paths.contains(fileName)) {
+      throw ArgumentError(
+        'Asset for key "$key" not found.',
+        'key',
+      );
+    }
+
+    return _webView.loadUrl(
+      Uri.file('/android_asset/$assetFilePath').toString(),
+      <String, String>{},
+    );
+  }
+
+  @override
+  Future<void> loadHtmlString(
+    String html, {
+    String? baseUrl,
+  }) {
+    return _webView.loadDataWithBaseUrl(
+      baseUrl: baseUrl,
+      data: html,
+      mimeType: 'text/html',
+    );
+  }
+
+  @override
+  Future<void> loadRequest(
+    LoadRequestParams params,
+  ) {
+    if (!params.uri.hasScheme) {
+      throw ArgumentError('WebViewRequest#uri is required to have a scheme.');
+    }
+    switch (params.method) {
+      case LoadRequestMethod.get:
+        return _webView.loadUrl(params.uri.toString(), params.headers);
+      case LoadRequestMethod.post:
+        return _webView.postUrl(
+            params.uri.toString(), params.body ?? Uint8List(0));
+      default:
+        throw UnimplementedError(
+          'This version of `AndroidWebViewController` currently has no implementation for HTTP method ${params.method.serialize()} in loadRequest.',
+        );
+    }
+  }
+
+  @override
+  Future<String?> currentUrl() => _webView.getUrl();
+
+  @override
+  Future<bool> canGoBack() => _webView.canGoBack();
+
+  @override
+  Future<bool> canGoForward() => _webView.canGoForward();
+
+  @override
+  Future<void> goBack() => _webView.goBack();
+
+  @override
+  Future<void> goForward() => _webView.goForward();
+
+  @override
+  Future<void> reload() => _webView.reload();
+
+  @override
+  Future<void> clearCache() => _webView.clearCache(true);
+
+  @override
+  Future<void> clearLocalStorage() =>
+      _androidWebViewParams.androidWebStorage.deleteAllData();
+
+  @override
+  Future<void> setPlatformNavigationDelegate(
+      covariant AndroidNavigationDelegate handler) async {
+    _currentNavigationDelegate = handler;
+    handler.setOnLoadRequest(loadRequest);
+    _webView.setWebViewClient(handler.androidWebViewClient);
+    _webView.setWebChromeClient(handler.androidWebChromeClient);
+    _webView.setDownloadListener(handler.androidDownloadListener);
+  }
+
+  @override
+  Future<void> runJavaScript(String javaScript) {
+    return _webView.evaluateJavascript(javaScript);
+  }
+
+  @override
+  Future<Object> runJavaScriptReturningResult(String javaScript) async {
+    final String? result = await _webView.evaluateJavascript(javaScript);
+
+    if (result == null) {
+      return '';
+    } else if (result == 'true') {
+      return true;
+    } else if (result == 'false') {
+      return false;
+    }
+
+    return num.tryParse(result) ?? result;
+  }
+
+  @override
+  Future<void> addJavaScriptChannel(
+    JavaScriptChannelParams javaScriptChannelParams,
+  ) {
+    final AndroidJavaScriptChannelParams androidJavaScriptParams =
+        javaScriptChannelParams is AndroidJavaScriptChannelParams
+            ? javaScriptChannelParams
+            : AndroidJavaScriptChannelParams.fromJavaScriptChannelParams(
+                javaScriptChannelParams);
+
+    // When JavaScript channel with the same name exists make sure to remove it
+    // before registering the new channel.
+    if (_javaScriptChannelParams.containsKey(androidJavaScriptParams.name)) {
+      _webView
+          .removeJavaScriptChannel(androidJavaScriptParams._javaScriptChannel);
+    }
+
+    _javaScriptChannelParams[androidJavaScriptParams.name] =
+        androidJavaScriptParams;
+
+    return _webView
+        .addJavaScriptChannel(androidJavaScriptParams._javaScriptChannel);
+  }
+
+  @override
+  Future<void> removeJavaScriptChannel(String javaScriptChannelName) async {
+    final AndroidJavaScriptChannelParams? javaScriptChannelParams =
+        _javaScriptChannelParams[javaScriptChannelName];
+    if (javaScriptChannelParams == null) {
+      return;
+    }
+
+    _javaScriptChannelParams.remove(javaScriptChannelName);
+    return _webView
+        .removeJavaScriptChannel(javaScriptChannelParams._javaScriptChannel);
+  }
+
+  @override
+  Future<String?> getTitle() => _webView.getTitle();
+
+  @override
+  Future<void> scrollTo(int x, int y) => _webView.scrollTo(x, y);
+
+  @override
+  Future<void> scrollBy(int x, int y) => _webView.scrollBy(x, y);
+
+  @override
+  Future<Offset> getScrollPosition() {
+    return _webView.getScrollPosition();
+  }
+
+  @override
+  Future<void> enableZoom(bool enabled) =>
+      _webView.settings.setSupportZoom(enabled);
+
+  @override
+  Future<void> setBackgroundColor(Color color) =>
+      _webView.setBackgroundColor(color);
+
+  @override
+  Future<void> setJavaScriptMode(JavaScriptMode javaScriptMode) =>
+      _webView.settings
+          .setJavaScriptEnabled(javaScriptMode == JavaScriptMode.unrestricted);
+
+  @override
+  Future<void> setUserAgent(String? userAgent) =>
+      _webView.settings.setUserAgentString(userAgent);
+
+  /// Sets the restrictions that apply on automatic media playback.
+  Future<void> setMediaPlaybackRequiresUserGesture(bool require) {
+    return _webView.settings.setMediaPlaybackRequiresUserGesture(require);
+  }
+}
+
+/// An implementation of [JavaScriptChannelParams] with the Android WebView API.
+///
+/// See [AndroidWebViewController.addJavaScriptChannel].
+@immutable
+class AndroidJavaScriptChannelParams extends JavaScriptChannelParams {
+  /// Constructs a [AndroidJavaScriptChannelParams].
+  AndroidJavaScriptChannelParams({
+    required super.name,
+    required super.onMessageReceived,
+    @visibleForTesting
+        AndroidWebViewProxy webViewProxy = const AndroidWebViewProxy(),
+  })  : assert(name.isNotEmpty),
+        _javaScriptChannel = webViewProxy.createJavaScriptChannel(
+          name,
+          postMessage: withWeakRefenceTo(
+            onMessageReceived,
+            (WeakReference<void Function(JavaScriptMessage)> weakReference) {
+              return (
+                String message,
+              ) {
+                if (weakReference.target != null) {
+                  weakReference.target!(
+                    JavaScriptMessage(message: message),
+                  );
+                }
+              };
+            },
+          ),
+        );
+
+  /// Constructs a [AndroidJavaScriptChannelParams] using a
+  /// [JavaScriptChannelParams].
+  AndroidJavaScriptChannelParams.fromJavaScriptChannelParams(
+    JavaScriptChannelParams params, {
+    @visibleForTesting
+        AndroidWebViewProxy webViewProxy = const AndroidWebViewProxy(),
+  }) : this(
+          name: params.name,
+          onMessageReceived: params.onMessageReceived,
+          webViewProxy: webViewProxy,
+        );
+
+  final android_webview.JavaScriptChannel _javaScriptChannel;
+}
+
+/// Object specifying creation parameters for creating a [AndroidWebViewWidget].
+///
+/// When adding additional fields make sure they can be null or have a default
+/// value to avoid breaking changes. See [PlatformWebViewWidgetCreationParams] for
+/// more information.
+@immutable
+class AndroidWebViewWidgetCreationParams
+    extends PlatformWebViewWidgetCreationParams {
+  /// Creates [AndroidWebWidgetCreationParams].
+  AndroidWebViewWidgetCreationParams({
+    super.key,
+    required super.controller,
+    super.layoutDirection,
+    super.gestureRecognizers,
+    @visibleForTesting InstanceManager? instanceManager,
+  }) : instanceManager = instanceManager ?? JavaObject.globalInstanceManager;
+
+  /// Constructs a [WebKitWebViewWidgetCreationParams] using a
+  /// [PlatformWebViewWidgetCreationParams].
+  AndroidWebViewWidgetCreationParams.fromPlatformWebViewWidgetCreationParams(
+    PlatformWebViewWidgetCreationParams params, {
+    InstanceManager? instanceManager,
+  }) : this(
+          key: params.key,
+          controller: params.controller,
+          layoutDirection: params.layoutDirection,
+          gestureRecognizers: params.gestureRecognizers,
+          instanceManager: instanceManager,
+        );
+
+  /// Maintains instances used to communicate with the native objects they
+  /// represent.
+  ///
+  /// This field is exposed for testing purposes only and should not be used
+  /// outside of tests.
+  @visibleForTesting
+  final InstanceManager instanceManager;
+}
+
+/// An implementation of [PlatformWebViewWidget] with the Android WebView API.
+class AndroidWebViewWidget extends PlatformWebViewWidget {
+  /// Constructs a [WebKitWebViewWidget].
+  AndroidWebViewWidget(PlatformWebViewWidgetCreationParams params)
+      : super.implementation(
+          params is AndroidWebViewWidgetCreationParams
+              ? params
+              : AndroidWebViewWidgetCreationParams
+                  .fromPlatformWebViewWidgetCreationParams(params),
+        );
+
+  AndroidWebViewWidgetCreationParams get _androidParams =>
+      params as AndroidWebViewWidgetCreationParams;
+
+  @override
+  Widget build(BuildContext context) {
+    return PlatformViewLink(
+        key: _androidParams.key,
+        viewType: 'plugins.flutter.io/webview',
+        surfaceFactory: (
+          BuildContext context,
+          PlatformViewController controller,
+        ) {
+          return AndroidViewSurface(
+            controller: controller as AndroidViewController,
+            gestureRecognizers: _androidParams.gestureRecognizers,
+            hitTestBehavior: PlatformViewHitTestBehavior.opaque,
+          );
+        },
+        onCreatePlatformView: (PlatformViewCreationParams params) {
+          return PlatformViewsService.initSurfaceAndroidView(
+            id: params.id,
+            viewType: 'plugins.flutter.io/webview',
+            layoutDirection: _androidParams.layoutDirection,
+            creationParams: _androidParams.instanceManager.getIdentifier(
+                (_androidParams.controller as AndroidWebViewController)
+                    ._webView),
+            creationParamsCodec: const StandardMessageCodec(),
+          )
+            ..addOnPlatformViewCreatedListener(params.onPlatformViewCreated)
+            ..create();
+        });
+  }
+}
diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_cookie_manager.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_cookie_manager.dart
new file mode 100644
index 0000000..5174ca5
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_cookie_manager.dart
@@ -0,0 +1,74 @@
+// 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.
+
+import 'package:flutter/foundation.dart';
+import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
+
+import 'android_webview.dart';
+
+/// Object specifying creation parameters for creating a [AndroidWebViewCookieManager].
+///
+/// When adding additional fields make sure they can be null or have a default
+/// value to avoid breaking changes. See [PlatformWebViewCookieManagerCreationParams] for
+/// more information.
+@immutable
+class AndroidWebViewCookieManagerCreationParams
+    extends PlatformWebViewCookieManagerCreationParams {
+  /// Creates a new [AndroidWebViewCookieManagerCreationParams] instance.
+  const AndroidWebViewCookieManagerCreationParams._(
+    // This parameter prevents breaking changes later.
+    // ignore: avoid_unused_constructor_parameters
+    PlatformWebViewCookieManagerCreationParams params,
+  ) : super();
+
+  /// Creates a [AndroidWebViewCookieManagerCreationParams] instance based on [PlatformWebViewCookieManagerCreationParams].
+  factory AndroidWebViewCookieManagerCreationParams.fromPlatformWebViewCookieManagerCreationParams(
+      PlatformWebViewCookieManagerCreationParams params) {
+    return AndroidWebViewCookieManagerCreationParams._(params);
+  }
+}
+
+/// Handles all cookie operations for the Android platform.
+class AndroidWebViewCookieManager extends PlatformWebViewCookieManager {
+  /// Creates a new [AndroidWebViewCookieManager].
+  AndroidWebViewCookieManager(
+    PlatformWebViewCookieManagerCreationParams params, {
+    CookieManager? cookieManager,
+  })  : _cookieManager = cookieManager ?? CookieManager.instance,
+        super.implementation(
+          params is AndroidWebViewCookieManagerCreationParams
+              ? params
+              : AndroidWebViewCookieManagerCreationParams
+                  .fromPlatformWebViewCookieManagerCreationParams(params),
+        );
+
+  final CookieManager _cookieManager;
+
+  @override
+  Future<bool> clearCookies() {
+    return _cookieManager.clearCookies();
+  }
+
+  @override
+  Future<void> setCookie(WebViewCookie cookie) {
+    if (!_isValidPath(cookie.path)) {
+      throw ArgumentError(
+          'The path property for the provided cookie was not given a legal value.');
+    }
+    return _cookieManager.setCookie(
+      cookie.domain,
+      '${Uri.encodeComponent(cookie.name)}=${Uri.encodeComponent(cookie.value)}; path=${cookie.path}',
+    );
+  }
+
+  bool _isValidPath(String path) {
+    // Permitted ranges based on RFC6265bis: https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-02#section-4.1.1
+    for (final int char in path.codeUnits) {
+      if ((char < 0x20 || char > 0x3A) && (char < 0x3C || char > 0x7E)) {
+        return false;
+      }
+    }
+    return true;
+  }
+}
diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_platform.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_platform.dart
new file mode 100644
index 0000000..24581eb
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_platform.dart
@@ -0,0 +1,45 @@
+// 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.
+
+import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
+
+import 'android_navigation_delegate.dart';
+import 'android_webview_controller.dart';
+import 'android_webview_cookie_manager.dart';
+
+/// Implementation of [WebViewPlatform] using the WebKit API.
+class AndroidWebViewPlatform extends WebViewPlatform {
+  /// Registers this class as the default instance of [WebViewPlatform].
+  static void registerWith() {
+    WebViewPlatform.instance = AndroidWebViewPlatform();
+  }
+
+  @override
+  AndroidWebViewController createPlatformWebViewController(
+    PlatformWebViewControllerCreationParams params,
+  ) {
+    return AndroidWebViewController(params);
+  }
+
+  @override
+  AndroidNavigationDelegate createPlatformNavigationDelegate(
+    PlatformNavigationDelegateCreationParams params,
+  ) {
+    return AndroidNavigationDelegate(params);
+  }
+
+  @override
+  AndroidWebViewWidget createPlatformWebViewWidget(
+    PlatformWebViewWidgetCreationParams params,
+  ) {
+    return AndroidWebViewWidget(params);
+  }
+
+  @override
+  AndroidWebViewCookieManager createPlatformCookieManager(
+    PlatformWebViewCookieManagerCreationParams params,
+  ) {
+    return AndroidWebViewCookieManager(params);
+  }
+}
diff --git a/packages/webview_flutter/webview_flutter_android/lib/webview_android.dart b/packages/webview_flutter/webview_flutter_android/lib/src/legacy/webview_android.dart
similarity index 94%
rename from packages/webview_flutter/webview_flutter_android/lib/webview_android.dart
rename to packages/webview_flutter/webview_flutter_android/lib/src/legacy/webview_android.dart
index 4774024..cfda749 100644
--- a/packages/webview_flutter/webview_flutter_android/lib/webview_android.dart
+++ b/packages/webview_flutter/webview_flutter_android/lib/src/legacy/webview_android.dart
@@ -8,9 +8,10 @@
 import 'package:flutter/gestures.dart';
 import 'package:flutter/services.dart';
 import 'package:flutter/widgets.dart';
-import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
+// ignore: implementation_imports
+import 'package:webview_flutter_platform_interface/src/webview_flutter_platform_interface_legacy.dart';
 
-import 'src/android_webview.dart';
+import '../android_webview.dart';
 import 'webview_android_widget.dart';
 
 /// Builds an Android webview.
diff --git a/packages/webview_flutter/webview_flutter_android/lib/webview_android_cookie_manager.dart b/packages/webview_flutter/webview_flutter_android/lib/src/legacy/webview_android_cookie_manager.dart
similarity index 85%
rename from packages/webview_flutter/webview_flutter_android/lib/webview_android_cookie_manager.dart
rename to packages/webview_flutter/webview_flutter_android/lib/src/legacy/webview_android_cookie_manager.dart
index 6e3f6f2..663a207 100644
--- a/packages/webview_flutter/webview_flutter_android/lib/webview_android_cookie_manager.dart
+++ b/packages/webview_flutter/webview_flutter_android/lib/src/legacy/webview_android_cookie_manager.dart
@@ -2,9 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
+// ignore: implementation_imports
+import 'package:webview_flutter_platform_interface/src/webview_flutter_platform_interface_legacy.dart';
 
-import 'src/android_webview.dart' as android_webview;
+import '../android_webview.dart' as android_webview;
 
 /// Handles all cookie operations for the current platform.
 class WebViewAndroidCookieManager extends WebViewCookieManagerPlatform {
diff --git a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart b/packages/webview_flutter/webview_flutter_android/lib/src/legacy/webview_android_widget.dart
similarity index 68%
rename from packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart
rename to packages/webview_flutter/webview_flutter_android/lib/src/legacy/webview_android_widget.dart
index 140d0da..fc1028e 100644
--- a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart
+++ b/packages/webview_flutter/webview_flutter_android/lib/src/legacy/webview_android_widget.dart
@@ -6,16 +6,18 @@
 import 'dart:typed_data';
 
 import 'package:flutter/widgets.dart';
-import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
+// ignore: implementation_imports
+import 'package:webview_flutter_platform_interface/src/webview_flutter_platform_interface_legacy.dart';
 
-import 'src/android_webview.dart' as android_webview;
+import '../android_webview.dart' as android_webview;
+import '../weak_reference_utils.dart';
 import 'webview_android_cookie_manager.dart';
 
 /// Creates a [Widget] with a [android_webview.WebView].
 class WebViewAndroidWidget extends StatefulWidget {
   /// Constructs a [WebViewAndroidWidget].
   const WebViewAndroidWidget({
-    Key? key,
+    super.key,
     required this.creationParams,
     required this.useHybridComposition,
     required this.callbacksHandler,
@@ -25,7 +27,7 @@
     @visibleForTesting
         this.flutterAssetManager = const android_webview.FlutterAssetManager(),
     @visibleForTesting this.webStorage,
-  }) : super(key: key);
+  });
 
   /// Initial parameters used to setup the WebView.
   final CreationParams creationParams;
@@ -86,12 +88,6 @@
   }
 
   @override
-  void dispose() {
-    super.dispose();
-    controller._dispose();
-  }
-
-  @override
   Widget build(BuildContext context) {
     return widget.onBuildWidget(controller);
   }
@@ -127,6 +123,7 @@
     _setCreationParams(creationParams);
     webView.setDownloadListener(downloadListener);
     webView.setWebChromeClient(webChromeClient);
+    webView.setWebViewClient(webViewClient);
 
     final String? initialUrl = creationParams.initialUrl;
     if (initialUrl != null) {
@@ -137,7 +134,61 @@
   final Map<String, WebViewAndroidJavaScriptChannel> _javaScriptChannels =
       <String, WebViewAndroidJavaScriptChannel>{};
 
-  late WebViewAndroidWebViewClient _webViewClient;
+  late final android_webview.WebViewClient _webViewClient = withWeakRefenceTo(
+      this, (WeakReference<WebViewAndroidPlatformController> weakReference) {
+    return webViewProxy.createWebViewClient(
+      onPageStarted: (_, String url) {
+        weakReference.target?.callbacksHandler.onPageStarted(url);
+      },
+      onPageFinished: (_, String url) {
+        weakReference.target?.callbacksHandler.onPageFinished(url);
+      },
+      onReceivedError: (
+        _,
+        int errorCode,
+        String description,
+        String failingUrl,
+      ) {
+        weakReference.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) {
+          weakReference.target?.callbacksHandler
+              .onWebResourceError(WebResourceError(
+            errorCode: error.errorCode,
+            description: error.description,
+            failingUrl: request.url,
+            errorType: _errorCodeToErrorType(error.errorCode),
+          ));
+        }
+      },
+      urlLoading: (_, String url) {
+        weakReference.target?._handleNavigationRequest(
+          url: url,
+          isForMainFrame: true,
+        );
+      },
+      requestLoading: (_, android_webview.WebResourceRequest request) {
+        weakReference.target?._handleNavigationRequest(
+          url: request.url,
+          isForMainFrame: request.isForMainFrame,
+        );
+      },
+    );
+  });
+
+  bool _hasNavigationDelegate = false;
+  bool _hasProgressTracking = false;
 
   /// Represents the WebView maintained by platform code.
   late final android_webview.WebView webView;
@@ -160,20 +211,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 +353,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 +435,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,34 +467,11 @@
         .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,
-      );
-    }
-    return webView.setWebViewClient(_webViewClient);
+    _hasNavigationDelegate = hasNavigationDelegate;
+    return _webViewClient.setSynchronousReturnValueForShouldOverrideUrlLoading(
+      hasNavigationDelegate,
+    );
   }
 
   Future<void> _setJavaScriptMode(JavascriptMode mode) {
@@ -444,114 +499,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 +541,54 @@
     );
   }
 
-  /// 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(
+    super.channelName,
+    this.javascriptChannelRegistry,
+  ) : super(
+          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.
@@ -713,6 +604,38 @@
     return android_webview.WebView(useHybridComposition: useHybridComposition);
   }
 
+  /// Constructs a [android_webview.WebViewClient].
+  android_webview.WebViewClient createWebViewClient({
+    void Function(android_webview.WebView webView, String url)? onPageStarted,
+    void Function(android_webview.WebView webView, String url)? onPageFinished,
+    void Function(
+      android_webview.WebView webView,
+      android_webview.WebResourceRequest request,
+      android_webview.WebResourceError error,
+    )?
+        onReceivedRequestError,
+    void Function(
+      android_webview.WebView webView,
+      int errorCode,
+      String description,
+      String failingUrl,
+    )?
+        onReceivedError,
+    void Function(android_webview.WebView webView,
+            android_webview.WebResourceRequest request)?
+        requestLoading,
+    void Function(android_webview.WebView webView, String url)? urlLoading,
+  }) {
+    return android_webview.WebViewClient(
+      onPageStarted: onPageStarted,
+      onPageFinished: onPageFinished,
+      onReceivedRequestError: onReceivedRequestError,
+      onReceivedError: onReceivedError,
+      requestLoading: requestLoading,
+      urlLoading: urlLoading,
+    );
+  }
+
   /// Enables debugging of web contents (HTML / CSS / JavaScript) loaded into any WebViews of this application.
   ///
   /// This flag can be enabled in order to facilitate debugging of web layouts
diff --git a/packages/webview_flutter/webview_flutter_android/lib/webview_surface_android.dart b/packages/webview_flutter/webview_flutter_android/lib/src/legacy/webview_surface_android.dart
similarity index 96%
rename from packages/webview_flutter/webview_flutter_android/lib/webview_surface_android.dart
rename to packages/webview_flutter/webview_flutter_android/lib/src/legacy/webview_surface_android.dart
index e89fb7d..8db2fe0 100644
--- a/packages/webview_flutter/webview_flutter_android/lib/webview_surface_android.dart
+++ b/packages/webview_flutter/webview_flutter_android/lib/src/legacy/webview_surface_android.dart
@@ -7,9 +7,10 @@
 import 'package:flutter/rendering.dart';
 import 'package:flutter/services.dart';
 import 'package:flutter/widgets.dart';
-import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
+// ignore: implementation_imports
+import 'package:webview_flutter_platform_interface/src/webview_flutter_platform_interface_legacy.dart';
 
-import 'src/android_webview.dart';
+import '../android_webview.dart';
 import 'webview_android.dart';
 import 'webview_android_widget.dart';
 
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/src/webview_flutter_android_legacy.dart b/packages/webview_flutter/webview_flutter_android/lib/src/webview_flutter_android_legacy.dart
new file mode 100644
index 0000000..a4f9166
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_android/lib/src/webview_flutter_android_legacy.dart
@@ -0,0 +1,7 @@
+// 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.
+
+export 'legacy/webview_android.dart';
+export 'legacy/webview_android_cookie_manager.dart';
+export 'legacy/webview_surface_android.dart';
diff --git a/packages/webview_flutter/webview_flutter_android/lib/webview_flutter_android.dart b/packages/webview_flutter/webview_flutter_android/lib/webview_flutter_android.dart
new file mode 100644
index 0000000..faab715
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_android/lib/webview_flutter_android.dart
@@ -0,0 +1,10 @@
+// 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.
+
+library webview_flutter_android;
+
+export 'src/android_navigation_delegate.dart';
+export 'src/android_webview_controller.dart';
+export 'src/android_webview_cookie_manager.dart';
+export 'src/android_webview_platform.dart';
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..d3adac8 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,20 +199,21 @@
 
 @FlutterApi()
 abstract class JavaScriptChannelFlutterApi {
-  void dispose(int instanceId);
-
   void postMessage(int instanceId, String message);
 }
 
 @HostApi(dartHostTestHandler: 'TestWebViewClientHostApi')
 abstract class WebViewClientHostApi {
-  void create(int instanceId, bool shouldOverrideUrlLoading);
+  void create(int instanceId);
+
+  void setSynchronousReturnValueForShouldOverrideUrlLoading(
+    int instanceId,
+    bool value,
+  );
 }
 
 @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 +249,6 @@
 
 @FlutterApi()
 abstract class DownloadListenerFlutterApi {
-  void dispose(int instanceId);
-
   void onDownloadStart(
     int instanceId,
     String url,
@@ -266,7 +261,7 @@
 
 @HostApi(dartHostTestHandler: 'TestWebChromeClientHostApi')
 abstract class WebChromeClientHostApi {
-  void create(int instanceId, int webViewClientInstanceId);
+  void create(int instanceId);
 }
 
 @HostApi(dartHostTestHandler: 'TestAssetManagerHostApi')
@@ -278,8 +273,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 e411b4e..4b22a1f 100644
--- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml
+++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml
@@ -2,10 +2,10 @@
 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.4
+version: 3.0.0
 
 environment:
-  sdk: ">=2.14.0 <3.0.0"
+  sdk: ">=2.17.0 <3.0.0"
   flutter: ">=3.0.0"
 
 flutter:
@@ -15,11 +15,12 @@
       android:
         package: io.flutter.plugins.webviewflutter
         pluginClass: WebViewFlutterPlugin
+        dartPluginClass: AndroidWebViewPlatform
 
 dependencies:
   flutter:
     sdk: flutter
-  webview_flutter_platform_interface: ^1.8.0
+  webview_flutter_platform_interface: ^2.0.0
 
 dev_dependencies:
   build_runner: ^2.1.4
@@ -27,5 +28,5 @@
     sdk: flutter
   flutter_test:
     sdk: flutter
-  mockito: ^5.2.0
-  pigeon: ^4.0.2
+  mockito: ^5.3.2
+  pigeon: ^4.2.3
diff --git a/packages/webview_flutter/webview_flutter_android/test/android_navigation_delegate_test.dart b/packages/webview_flutter/webview_flutter_android/test/android_navigation_delegate_test.dart
new file mode 100644
index 0000000..26d4e68
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_android/test/android_navigation_delegate_test.dart
@@ -0,0 +1,514 @@
+// 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.
+
+import 'dart:async';
+
+import 'package:flutter_test/flutter_test.dart';
+import 'package:webview_flutter_android/src/android_proxy.dart';
+import 'package:webview_flutter_android/src/android_webview.dart'
+    as android_webview;
+import 'package:webview_flutter_android/webview_flutter_android.dart';
+import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
+
+void main() {
+  group('AndroidNavigationDelegate', () {
+    test('onPageFinished', () {
+      final AndroidNavigationDelegate androidNavigationDelegate =
+          AndroidNavigationDelegate(_buildCreationParams());
+
+      late final String callbackUrl;
+      androidNavigationDelegate
+          .setOnPageFinished((String url) => callbackUrl = url);
+
+      CapturingWebViewClient.lastCreatedDelegate.onPageFinished!(
+        android_webview.WebView.detached(),
+        'https://www.google.com',
+      );
+
+      expect(callbackUrl, 'https://www.google.com');
+    });
+
+    test('onPageStarted', () {
+      final AndroidNavigationDelegate androidNavigationDelegate =
+          AndroidNavigationDelegate(_buildCreationParams());
+
+      late final String callbackUrl;
+      androidNavigationDelegate
+          .setOnPageStarted((String url) => callbackUrl = url);
+
+      CapturingWebViewClient.lastCreatedDelegate.onPageStarted!(
+        android_webview.WebView.detached(),
+        'https://www.google.com',
+      );
+
+      expect(callbackUrl, 'https://www.google.com');
+    });
+
+    test('onWebResourceError from onReceivedRequestError', () {
+      final AndroidNavigationDelegate androidNavigationDelegate =
+          AndroidNavigationDelegate(_buildCreationParams());
+
+      late final WebResourceError callbackError;
+      androidNavigationDelegate.setOnWebResourceError(
+          (WebResourceError error) => callbackError = error);
+
+      CapturingWebViewClient.lastCreatedDelegate.onReceivedRequestError!(
+        android_webview.WebView.detached(),
+        android_webview.WebResourceRequest(
+          url: 'https://www.google.com',
+          isForMainFrame: false,
+          isRedirect: true,
+          hasGesture: true,
+          method: 'GET',
+          requestHeaders: <String, String>{'X-Mock': 'mocking'},
+        ),
+        android_webview.WebResourceError(
+          errorCode: android_webview.WebViewClient.errorFileNotFound,
+          description: 'Page not found.',
+        ),
+      );
+
+      expect(callbackError.errorCode,
+          android_webview.WebViewClient.errorFileNotFound);
+      expect(callbackError.description, 'Page not found.');
+      expect(callbackError.errorType, WebResourceErrorType.fileNotFound);
+      expect(callbackError.isForMainFrame, false);
+    });
+
+    test('onWebResourceError from onRequestError', () {
+      final AndroidNavigationDelegate androidNavigationDelegate =
+          AndroidNavigationDelegate(_buildCreationParams());
+
+      late final WebResourceError callbackError;
+      androidNavigationDelegate.setOnWebResourceError(
+          (WebResourceError error) => callbackError = error);
+
+      CapturingWebViewClient.lastCreatedDelegate.onReceivedError!(
+        android_webview.WebView.detached(),
+        android_webview.WebViewClient.errorFileNotFound,
+        'Page not found.',
+        'https://www.google.com',
+      );
+
+      expect(callbackError.errorCode,
+          android_webview.WebViewClient.errorFileNotFound);
+      expect(callbackError.description, 'Page not found.');
+      expect(callbackError.errorType, WebResourceErrorType.fileNotFound);
+      expect(callbackError.isForMainFrame, true);
+    });
+
+    test(
+        'onNavigationRequest from requestLoading should not be called when loadUrlCallback is not specified',
+        () {
+      final AndroidNavigationDelegate androidNavigationDelegate =
+          AndroidNavigationDelegate(_buildCreationParams());
+
+      NavigationRequest? callbackNavigationRequest;
+      androidNavigationDelegate
+          .setOnNavigationRequest((NavigationRequest navigationRequest) {
+        callbackNavigationRequest = navigationRequest;
+        return NavigationDecision.prevent;
+      });
+
+      CapturingWebViewClient.lastCreatedDelegate.requestLoading!(
+        android_webview.WebView.detached(),
+        android_webview.WebResourceRequest(
+          url: 'https://www.google.com',
+          isForMainFrame: true,
+          isRedirect: true,
+          hasGesture: true,
+          method: 'GET',
+          requestHeaders: <String, String>{'X-Mock': 'mocking'},
+        ),
+      );
+
+      expect(callbackNavigationRequest, isNull);
+    });
+
+    test(
+        'onLoadRequest from requestLoading should not be called when navigationRequestCallback is not specified',
+        () {
+      final Completer<void> completer = Completer<void>();
+      final AndroidNavigationDelegate androidNavigationDelegate =
+          AndroidNavigationDelegate(_buildCreationParams());
+
+      androidNavigationDelegate.setOnLoadRequest((_) {
+        completer.complete();
+        return completer.future;
+      });
+
+      CapturingWebViewClient.lastCreatedDelegate.requestLoading!(
+        android_webview.WebView.detached(),
+        android_webview.WebResourceRequest(
+          url: 'https://www.google.com',
+          isForMainFrame: true,
+          isRedirect: true,
+          hasGesture: true,
+          method: 'GET',
+          requestHeaders: <String, String>{'X-Mock': 'mocking'},
+        ),
+      );
+
+      expect(completer.isCompleted, false);
+    });
+
+    test(
+        'onLoadRequest from requestLoading should not be called when onNavigationRequestCallback returns NavigationDecision.prevent',
+        () {
+      final Completer<void> completer = Completer<void>();
+      final AndroidNavigationDelegate androidNavigationDelegate =
+          AndroidNavigationDelegate(_buildCreationParams());
+
+      androidNavigationDelegate.setOnLoadRequest((_) {
+        completer.complete();
+        return completer.future;
+      });
+
+      late final NavigationRequest callbackNavigationRequest;
+      androidNavigationDelegate
+          .setOnNavigationRequest((NavigationRequest navigationRequest) {
+        callbackNavigationRequest = navigationRequest;
+        return NavigationDecision.prevent;
+      });
+
+      CapturingWebViewClient.lastCreatedDelegate.requestLoading!(
+        android_webview.WebView.detached(),
+        android_webview.WebResourceRequest(
+          url: 'https://www.google.com',
+          isForMainFrame: true,
+          isRedirect: true,
+          hasGesture: true,
+          method: 'GET',
+          requestHeaders: <String, String>{'X-Mock': 'mocking'},
+        ),
+      );
+
+      expect(callbackNavigationRequest.isMainFrame, true);
+      expect(callbackNavigationRequest.url, 'https://www.google.com');
+      expect(completer.isCompleted, false);
+    });
+
+    test(
+        'onLoadRequest from requestLoading should complete when onNavigationRequestCallback returns NavigationDecision.navigate',
+        () {
+      final Completer<void> completer = Completer<void>();
+      late final LoadRequestParams loadRequestParams;
+      final AndroidNavigationDelegate androidNavigationDelegate =
+          AndroidNavigationDelegate(_buildCreationParams());
+
+      androidNavigationDelegate.setOnLoadRequest((LoadRequestParams params) {
+        loadRequestParams = params;
+        completer.complete();
+        return completer.future;
+      });
+
+      late final NavigationRequest callbackNavigationRequest;
+      androidNavigationDelegate
+          .setOnNavigationRequest((NavigationRequest navigationRequest) {
+        callbackNavigationRequest = navigationRequest;
+        return NavigationDecision.navigate;
+      });
+
+      CapturingWebViewClient.lastCreatedDelegate.requestLoading!(
+        android_webview.WebView.detached(),
+        android_webview.WebResourceRequest(
+          url: 'https://www.google.com',
+          isForMainFrame: true,
+          isRedirect: true,
+          hasGesture: true,
+          method: 'GET',
+          requestHeaders: <String, String>{'X-Mock': 'mocking'},
+        ),
+      );
+
+      expect(loadRequestParams.uri.toString(), 'https://www.google.com');
+      expect(loadRequestParams.headers, <String, String>{'X-Mock': 'mocking'});
+      expect(callbackNavigationRequest.isMainFrame, true);
+      expect(callbackNavigationRequest.url, 'https://www.google.com');
+      expect(completer.isCompleted, true);
+    });
+
+    test(
+        'onNavigationRequest from urlLoading should not be called when loadUrlCallback is not specified',
+        () {
+      final AndroidNavigationDelegate androidNavigationDelegate =
+          AndroidNavigationDelegate(_buildCreationParams());
+
+      NavigationRequest? callbackNavigationRequest;
+      androidNavigationDelegate
+          .setOnNavigationRequest((NavigationRequest navigationRequest) {
+        callbackNavigationRequest = navigationRequest;
+        return NavigationDecision.prevent;
+      });
+
+      CapturingWebViewClient.lastCreatedDelegate.urlLoading!(
+        android_webview.WebView.detached(),
+        'https://www.google.com',
+      );
+
+      expect(callbackNavigationRequest, isNull);
+    });
+
+    test(
+        'onLoadRequest from urlLoading should not be called when navigationRequestCallback is not specified',
+        () {
+      final Completer<void> completer = Completer<void>();
+      final AndroidNavigationDelegate androidNavigationDelegate =
+          AndroidNavigationDelegate(_buildCreationParams());
+
+      androidNavigationDelegate.setOnLoadRequest((_) {
+        completer.complete();
+        return completer.future;
+      });
+
+      CapturingWebViewClient.lastCreatedDelegate.urlLoading!(
+        android_webview.WebView.detached(),
+        'https://www.google.com',
+      );
+
+      expect(completer.isCompleted, false);
+    });
+
+    test(
+        'onLoadRequest from urlLoading should not be called when onNavigationRequestCallback returns NavigationDecision.prevent',
+        () {
+      final Completer<void> completer = Completer<void>();
+      final AndroidNavigationDelegate androidNavigationDelegate =
+          AndroidNavigationDelegate(_buildCreationParams());
+
+      androidNavigationDelegate.setOnLoadRequest((_) {
+        completer.complete();
+        return completer.future;
+      });
+
+      late final NavigationRequest callbackNavigationRequest;
+      androidNavigationDelegate
+          .setOnNavigationRequest((NavigationRequest navigationRequest) {
+        callbackNavigationRequest = navigationRequest;
+        return NavigationDecision.prevent;
+      });
+
+      CapturingWebViewClient.lastCreatedDelegate.urlLoading!(
+        android_webview.WebView.detached(),
+        'https://www.google.com',
+      );
+
+      expect(callbackNavigationRequest.isMainFrame, true);
+      expect(callbackNavigationRequest.url, 'https://www.google.com');
+      expect(completer.isCompleted, false);
+    });
+
+    test(
+        'onLoadRequest from urlLoading should complete when onNavigationRequestCallback returns NavigationDecision.navigate',
+        () {
+      final Completer<void> completer = Completer<void>();
+      late final LoadRequestParams loadRequestParams;
+      final AndroidNavigationDelegate androidNavigationDelegate =
+          AndroidNavigationDelegate(_buildCreationParams());
+
+      androidNavigationDelegate.setOnLoadRequest((LoadRequestParams params) {
+        loadRequestParams = params;
+        completer.complete();
+        return completer.future;
+      });
+
+      late final NavigationRequest callbackNavigationRequest;
+      androidNavigationDelegate
+          .setOnNavigationRequest((NavigationRequest navigationRequest) {
+        callbackNavigationRequest = navigationRequest;
+        return NavigationDecision.navigate;
+      });
+
+      CapturingWebViewClient.lastCreatedDelegate.urlLoading!(
+        android_webview.WebView.detached(),
+        'https://www.google.com',
+      );
+
+      expect(loadRequestParams.uri.toString(), 'https://www.google.com');
+      expect(loadRequestParams.headers, <String, String>{});
+      expect(callbackNavigationRequest.isMainFrame, true);
+      expect(callbackNavigationRequest.url, 'https://www.google.com');
+      expect(completer.isCompleted, true);
+    });
+
+    test('setOnNavigationRequest should override URL loading', () {
+      final AndroidNavigationDelegate androidNavigationDelegate =
+          AndroidNavigationDelegate(_buildCreationParams());
+
+      androidNavigationDelegate.setOnNavigationRequest(
+        (NavigationRequest request) => NavigationDecision.navigate,
+      );
+
+      expect(
+          CapturingWebViewClient.lastCreatedDelegate
+              .synchronousReturnValueForShouldOverrideUrlLoading,
+          isTrue);
+    });
+
+    test('onProgress', () {
+      final AndroidNavigationDelegate androidNavigationDelegate =
+          AndroidNavigationDelegate(_buildCreationParams());
+
+      late final int callbackProgress;
+      androidNavigationDelegate
+          .setOnProgress((int progress) => callbackProgress = progress);
+
+      CapturingWebChromeClient.lastCreatedDelegate.onProgressChanged!(
+        android_webview.WebView.detached(),
+        42,
+      );
+
+      expect(callbackProgress, 42);
+    });
+
+    test(
+        'onLoadRequest from onDownloadStart should not be called when navigationRequestCallback is not specified',
+        () {
+      final Completer<void> completer = Completer<void>();
+      final AndroidNavigationDelegate androidNavigationDelegate =
+          AndroidNavigationDelegate(_buildCreationParams());
+
+      androidNavigationDelegate.setOnLoadRequest((_) {
+        completer.complete();
+        return completer.future;
+      });
+
+      CapturingDownloadListener.lastCreatedListener.onDownloadStart(
+        '',
+        '',
+        '',
+        '',
+        0,
+      );
+
+      expect(completer.isCompleted, false);
+    });
+
+    test(
+        'onLoadRequest from onDownloadStart should not be called when onNavigationRequestCallback returns NavigationDecision.prevent',
+        () {
+      final Completer<void> completer = Completer<void>();
+      final AndroidNavigationDelegate androidNavigationDelegate =
+          AndroidNavigationDelegate(_buildCreationParams());
+
+      androidNavigationDelegate.setOnLoadRequest((_) {
+        completer.complete();
+        return completer.future;
+      });
+
+      late final NavigationRequest callbackNavigationRequest;
+      androidNavigationDelegate
+          .setOnNavigationRequest((NavigationRequest navigationRequest) {
+        callbackNavigationRequest = navigationRequest;
+        return NavigationDecision.prevent;
+      });
+
+      CapturingDownloadListener.lastCreatedListener.onDownloadStart(
+        'https://www.google.com',
+        '',
+        '',
+        '',
+        0,
+      );
+
+      expect(callbackNavigationRequest.isMainFrame, true);
+      expect(callbackNavigationRequest.url, 'https://www.google.com');
+      expect(completer.isCompleted, false);
+    });
+
+    test(
+        'onLoadRequest from onDownloadStart should complete when onNavigationRequestCallback returns NavigationDecision.navigate',
+        () {
+      final Completer<void> completer = Completer<void>();
+      late final LoadRequestParams loadRequestParams;
+      final AndroidNavigationDelegate androidNavigationDelegate =
+          AndroidNavigationDelegate(_buildCreationParams());
+
+      androidNavigationDelegate.setOnLoadRequest((LoadRequestParams params) {
+        loadRequestParams = params;
+        completer.complete();
+        return completer.future;
+      });
+
+      late final NavigationRequest callbackNavigationRequest;
+      androidNavigationDelegate
+          .setOnNavigationRequest((NavigationRequest navigationRequest) {
+        callbackNavigationRequest = navigationRequest;
+        return NavigationDecision.navigate;
+      });
+
+      CapturingDownloadListener.lastCreatedListener.onDownloadStart(
+        'https://www.google.com',
+        '',
+        '',
+        '',
+        0,
+      );
+
+      expect(loadRequestParams.uri.toString(), 'https://www.google.com');
+      expect(loadRequestParams.headers, <String, String>{});
+      expect(callbackNavigationRequest.isMainFrame, true);
+      expect(callbackNavigationRequest.url, 'https://www.google.com');
+      expect(completer.isCompleted, true);
+    });
+  });
+}
+
+AndroidNavigationDelegateCreationParams _buildCreationParams() {
+  return AndroidNavigationDelegateCreationParams
+      .fromPlatformNavigationDelegateCreationParams(
+    const PlatformNavigationDelegateCreationParams(),
+    androidWebViewProxy: const AndroidWebViewProxy(
+      createAndroidWebChromeClient: CapturingWebChromeClient.new,
+      createAndroidWebViewClient: CapturingWebViewClient.new,
+      createDownloadListener: CapturingDownloadListener.new,
+    ),
+  );
+}
+
+// Records the last created instance of itself.
+class CapturingWebViewClient extends android_webview.WebViewClient {
+  CapturingWebViewClient({
+    super.onPageFinished,
+    super.onPageStarted,
+    super.onReceivedError,
+    super.onReceivedRequestError,
+    super.requestLoading,
+    super.urlLoading,
+  }) : super.detached() {
+    lastCreatedDelegate = this;
+  }
+
+  static CapturingWebViewClient lastCreatedDelegate = CapturingWebViewClient();
+
+  bool synchronousReturnValueForShouldOverrideUrlLoading = false;
+
+  @override
+  Future<void> setSynchronousReturnValueForShouldOverrideUrlLoading(
+      bool value) async {
+    synchronousReturnValueForShouldOverrideUrlLoading = value;
+  }
+}
+
+// Records the last created instance of itself.
+class CapturingWebChromeClient extends android_webview.WebChromeClient {
+  CapturingWebChromeClient({
+    super.onProgressChanged,
+  }) : super.detached() {
+    lastCreatedDelegate = this;
+  }
+  static CapturingWebChromeClient lastCreatedDelegate =
+      CapturingWebChromeClient();
+}
+
+// Records the last created instance of itself.
+class CapturingDownloadListener extends android_webview.DownloadListener {
+  CapturingDownloadListener({
+    required super.onDownloadStart,
+  }) : super.detached() {
+    lastCreatedListener = this;
+  }
+  static CapturingDownloadListener lastCreatedListener =
+      CapturingDownloadListener(onDownloadStart: (_, __, ___, ____, _____) {});
+}
diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_controller_test.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_controller_test.dart
new file mode 100644
index 0000000..4b74f3a
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_controller_test.dart
@@ -0,0 +1,798 @@
+// 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.
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mockito/annotations.dart';
+import 'package:mockito/mockito.dart';
+import 'package:webview_flutter_android/src/android_proxy.dart';
+import 'package:webview_flutter_android/src/android_webview.dart'
+    as android_webview;
+import 'package:webview_flutter_android/src/instance_manager.dart';
+import 'package:webview_flutter_android/webview_flutter_android.dart';
+import 'package:webview_flutter_platform_interface/src/webview_platform.dart';
+
+import 'android_webview_controller_test.mocks.dart';
+
+@GenerateNiceMocks(<MockSpec<Object>>[
+  MockSpec<AndroidNavigationDelegate>(),
+  MockSpec<AndroidWebViewController>(),
+  MockSpec<AndroidWebViewProxy>(),
+  MockSpec<AndroidWebViewWidgetCreationParams>(),
+  MockSpec<android_webview.FlutterAssetManager>(),
+  MockSpec<android_webview.JavaScriptChannel>(),
+  MockSpec<android_webview.WebChromeClient>(),
+  MockSpec<android_webview.WebSettings>(),
+  MockSpec<android_webview.WebView>(),
+  MockSpec<android_webview.WebViewClient>(),
+  MockSpec<android_webview.WebStorage>(),
+  MockSpec<InstanceManager>(),
+])
+void main() {
+  TestWidgetsFlutterBinding.ensureInitialized();
+
+  AndroidWebViewController createControllerWithMocks({
+    android_webview.FlutterAssetManager? mockFlutterAssetManager,
+    android_webview.JavaScriptChannel? mockJavaScriptChannel,
+    android_webview.WebChromeClient? mockWebChromeClient,
+    android_webview.WebView? mockWebView,
+    android_webview.WebViewClient? mockWebViewClient,
+    android_webview.WebStorage? mockWebStorage,
+    android_webview.WebSettings? mockSettings,
+  }) {
+    final android_webview.WebView nonNullMockWebView =
+        mockWebView ?? MockWebView();
+
+    final AndroidWebViewControllerCreationParams creationParams =
+        AndroidWebViewControllerCreationParams(
+            androidWebStorage: mockWebStorage ?? MockWebStorage(),
+            androidWebViewProxy: AndroidWebViewProxy(
+              createAndroidWebChromeClient: (
+                      {void Function(android_webview.WebView, int)?
+                          onProgressChanged}) =>
+                  mockWebChromeClient ?? MockWebChromeClient(),
+              createAndroidWebView: ({required bool useHybridComposition}) =>
+                  nonNullMockWebView,
+              createAndroidWebViewClient: ({
+                void Function(android_webview.WebView webView, String url)?
+                    onPageFinished,
+                void Function(android_webview.WebView webView, String url)?
+                    onPageStarted,
+                @Deprecated('Only called on Android version < 23.')
+                    void Function(
+                  android_webview.WebView webView,
+                  int errorCode,
+                  String description,
+                  String failingUrl,
+                )?
+                        onReceivedError,
+                void Function(
+                  android_webview.WebView webView,
+                  android_webview.WebResourceRequest request,
+                  android_webview.WebResourceError error,
+                )?
+                    onReceivedRequestError,
+                void Function(
+                  android_webview.WebView webView,
+                  android_webview.WebResourceRequest request,
+                )?
+                    requestLoading,
+                void Function(android_webview.WebView webView, String url)?
+                    urlLoading,
+              }) =>
+                  mockWebViewClient ?? MockWebViewClient(),
+              createFlutterAssetManager: () =>
+                  mockFlutterAssetManager ?? MockFlutterAssetManager(),
+              createJavaScriptChannel: (
+                String channelName, {
+                required void Function(String) postMessage,
+              }) =>
+                  mockJavaScriptChannel ?? MockJavaScriptChannel(),
+            ));
+
+    when(nonNullMockWebView.settings)
+        .thenReturn(mockSettings ?? MockWebSettings());
+
+    return AndroidWebViewController(creationParams);
+  }
+
+  group('AndroidWebViewController', () {
+    AndroidJavaScriptChannelParams
+        createAndroidJavaScriptChannelParamsWithMocks({
+      String? name,
+      MockJavaScriptChannel? mockJavaScriptChannel,
+    }) {
+      return AndroidJavaScriptChannelParams(
+          name: name ?? 'test',
+          onMessageReceived: (JavaScriptMessage message) {},
+          webViewProxy: AndroidWebViewProxy(
+            createJavaScriptChannel: (
+              String channelName, {
+              required void Function(String) postMessage,
+            }) =>
+                mockJavaScriptChannel ?? MockJavaScriptChannel(),
+          ));
+    }
+
+    test('loadFile without file prefix', () async {
+      final MockWebView mockWebView = MockWebView();
+      final MockWebSettings mockWebSettings = MockWebSettings();
+      createControllerWithMocks(
+        mockWebView: mockWebView,
+        mockSettings: mockWebSettings,
+      );
+
+      verify(mockWebSettings.setBuiltInZoomControls(true)).called(1);
+      verify(mockWebSettings.setDisplayZoomControls(false)).called(1);
+      verify(mockWebSettings.setDomStorageEnabled(true)).called(1);
+      verify(mockWebSettings.setJavaScriptCanOpenWindowsAutomatically(true))
+          .called(1);
+      verify(mockWebSettings.setLoadWithOverviewMode(true)).called(1);
+      verify(mockWebSettings.setSupportMultipleWindows(true)).called(1);
+      verify(mockWebSettings.setUseWideViewPort(true)).called(1);
+    });
+
+    test('loadFile without file prefix', () async {
+      final MockWebView mockWebView = MockWebView();
+      final MockWebSettings mockWebSettings = MockWebSettings();
+      final AndroidWebViewController controller = createControllerWithMocks(
+        mockWebView: mockWebView,
+        mockSettings: mockWebSettings,
+      );
+
+      await controller.loadFile('/path/to/file.html');
+
+      verify(mockWebSettings.setAllowFileAccess(true)).called(1);
+      verify(mockWebView.loadUrl(
+        'file:///path/to/file.html',
+        <String, String>{},
+      )).called(1);
+    });
+
+    test('loadFile without file prefix and characters to be escaped', () async {
+      final MockWebView mockWebView = MockWebView();
+      final MockWebSettings mockWebSettings = MockWebSettings();
+      final AndroidWebViewController controller = createControllerWithMocks(
+        mockWebView: mockWebView,
+        mockSettings: mockWebSettings,
+      );
+
+      await controller.loadFile('/path/to/?_<_>_.html');
+
+      verify(mockWebSettings.setAllowFileAccess(true)).called(1);
+      verify(mockWebView.loadUrl(
+        'file:///path/to/%3F_%3C_%3E_.html',
+        <String, String>{},
+      )).called(1);
+    });
+
+    test('loadFile with file prefix', () async {
+      final MockWebView mockWebView = MockWebView();
+      final MockWebSettings mockWebSettings = MockWebSettings();
+      final AndroidWebViewController controller = createControllerWithMocks(
+        mockWebView: mockWebView,
+      );
+
+      when(mockWebView.settings).thenReturn(mockWebSettings);
+
+      await controller.loadFile('file:///path/to/file.html');
+
+      verify(mockWebSettings.setAllowFileAccess(true)).called(1);
+      verify(mockWebView.loadUrl(
+        'file:///path/to/file.html',
+        <String, String>{},
+      )).called(1);
+    });
+
+    test('loadFlutterAsset when asset does not exists', () async {
+      final MockWebView mockWebView = MockWebView();
+      final MockFlutterAssetManager mockAssetManager =
+          MockFlutterAssetManager();
+      final AndroidWebViewController controller = createControllerWithMocks(
+        mockFlutterAssetManager: mockAssetManager,
+        mockWebView: mockWebView,
+      );
+
+      when(mockAssetManager.getAssetFilePathByName('mock_key'))
+          .thenAnswer((_) => Future<String>.value(''));
+      when(mockAssetManager.list(''))
+          .thenAnswer((_) => Future<List<String>>.value(<String>[]));
+
+      try {
+        await controller.loadFlutterAsset('mock_key');
+        fail('Expected an `ArgumentError`.');
+      } on ArgumentError catch (e) {
+        expect(e.message, 'Asset for key "mock_key" not found.');
+        expect(e.name, 'key');
+      } on Error {
+        fail('Expect an `ArgumentError`.');
+      }
+
+      verify(mockAssetManager.getAssetFilePathByName('mock_key')).called(1);
+      verify(mockAssetManager.list('')).called(1);
+      verifyNever(mockWebView.loadUrl(any, any));
+    });
+
+    test('loadFlutterAsset when asset does exists', () async {
+      final MockWebView mockWebView = MockWebView();
+      final MockFlutterAssetManager mockAssetManager =
+          MockFlutterAssetManager();
+      final AndroidWebViewController controller = createControllerWithMocks(
+        mockFlutterAssetManager: mockAssetManager,
+        mockWebView: mockWebView,
+      );
+
+      when(mockAssetManager.getAssetFilePathByName('mock_key'))
+          .thenAnswer((_) => Future<String>.value('www/mock_file.html'));
+      when(mockAssetManager.list('www')).thenAnswer(
+          (_) => Future<List<String>>.value(<String>['mock_file.html']));
+
+      await controller.loadFlutterAsset('mock_key');
+
+      verify(mockAssetManager.getAssetFilePathByName('mock_key')).called(1);
+      verify(mockAssetManager.list('www')).called(1);
+      verify(mockWebView.loadUrl(
+          'file:///android_asset/www/mock_file.html', <String, String>{}));
+    });
+
+    test(
+        'loadFlutterAsset when asset name contains characters that should be escaped',
+        () async {
+      final MockWebView mockWebView = MockWebView();
+      final MockFlutterAssetManager mockAssetManager =
+          MockFlutterAssetManager();
+      final AndroidWebViewController controller = createControllerWithMocks(
+        mockFlutterAssetManager: mockAssetManager,
+        mockWebView: mockWebView,
+      );
+
+      when(mockAssetManager.getAssetFilePathByName('mock_key'))
+          .thenAnswer((_) => Future<String>.value('www/?_<_>_.html'));
+      when(mockAssetManager.list('www')).thenAnswer(
+          (_) => Future<List<String>>.value(<String>['?_<_>_.html']));
+
+      await controller.loadFlutterAsset('mock_key');
+
+      verify(mockAssetManager.getAssetFilePathByName('mock_key')).called(1);
+      verify(mockAssetManager.list('www')).called(1);
+      verify(mockWebView.loadUrl(
+          'file:///android_asset/www/%3F_%3C_%3E_.html', <String, String>{}));
+    });
+
+    test('loadHtmlString without baseUrl', () async {
+      final MockWebView mockWebView = MockWebView();
+      final AndroidWebViewController controller = createControllerWithMocks(
+        mockWebView: mockWebView,
+      );
+
+      await controller.loadHtmlString('<p>Hello Test!</p>');
+
+      verify(mockWebView.loadDataWithBaseUrl(
+        data: '<p>Hello Test!</p>',
+        mimeType: 'text/html',
+      )).called(1);
+    });
+
+    test('loadHtmlString with baseUrl', () async {
+      final MockWebView mockWebView = MockWebView();
+      final AndroidWebViewController controller = createControllerWithMocks(
+        mockWebView: mockWebView,
+      );
+
+      await controller.loadHtmlString('<p>Hello Test!</p>',
+          baseUrl: 'https://flutter.dev');
+
+      verify(mockWebView.loadDataWithBaseUrl(
+        data: '<p>Hello Test!</p>',
+        baseUrl: 'https://flutter.dev',
+        mimeType: 'text/html',
+      )).called(1);
+    });
+
+    test('loadRequest without URI scheme', () async {
+      final MockWebView mockWebView = MockWebView();
+      final AndroidWebViewController controller = createControllerWithMocks(
+        mockWebView: mockWebView,
+      );
+      final LoadRequestParams requestParams = LoadRequestParams(
+        uri: Uri.parse('flutter.dev'),
+      );
+
+      try {
+        await controller.loadRequest(requestParams);
+        fail('Expect an `ArgumentError`.');
+      } on ArgumentError catch (e) {
+        expect(e.message, 'WebViewRequest#uri is required to have a scheme.');
+      } on Error {
+        fail('Expect a `ArgumentError`.');
+      }
+
+      verifyNever(mockWebView.loadUrl(any, any));
+      verifyNever(mockWebView.postUrl(any, any));
+    });
+
+    test('loadRequest using the GET method', () async {
+      final MockWebView mockWebView = MockWebView();
+      final AndroidWebViewController controller = createControllerWithMocks(
+        mockWebView: mockWebView,
+      );
+      final LoadRequestParams requestParams = LoadRequestParams(
+        uri: Uri.parse('https://flutter.dev'),
+        headers: const <String, String>{'X-Test': 'Testing'},
+      );
+
+      await controller.loadRequest(requestParams);
+
+      verify(mockWebView.loadUrl(
+        'https://flutter.dev',
+        <String, String>{'X-Test': 'Testing'},
+      ));
+      verifyNever(mockWebView.postUrl(any, any));
+    });
+
+    test('loadRequest using the POST method without body', () async {
+      final MockWebView mockWebView = MockWebView();
+      final AndroidWebViewController controller = createControllerWithMocks(
+        mockWebView: mockWebView,
+      );
+      final LoadRequestParams requestParams = LoadRequestParams(
+        uri: Uri.parse('https://flutter.dev'),
+        method: LoadRequestMethod.post,
+        headers: const <String, String>{'X-Test': 'Testing'},
+      );
+
+      await controller.loadRequest(requestParams);
+
+      verify(mockWebView.postUrl(
+        'https://flutter.dev',
+        Uint8List(0),
+      ));
+      verifyNever(mockWebView.loadUrl(any, any));
+    });
+
+    test('loadRequest using the POST method with body', () async {
+      final MockWebView mockWebView = MockWebView();
+      final AndroidWebViewController controller = createControllerWithMocks(
+        mockWebView: mockWebView,
+      );
+      final LoadRequestParams requestParams = LoadRequestParams(
+        uri: Uri.parse('https://flutter.dev'),
+        method: LoadRequestMethod.post,
+        headers: const <String, String>{'X-Test': 'Testing'},
+        body: Uint8List.fromList('{"message": "Hello World!"}'.codeUnits),
+      );
+
+      await controller.loadRequest(requestParams);
+
+      verify(mockWebView.postUrl(
+        'https://flutter.dev',
+        Uint8List.fromList('{"message": "Hello World!"}'.codeUnits),
+      ));
+      verifyNever(mockWebView.loadUrl(any, any));
+    });
+
+    test('currentUrl', () async {
+      final MockWebView mockWebView = MockWebView();
+      final AndroidWebViewController controller = createControllerWithMocks(
+        mockWebView: mockWebView,
+      );
+
+      await controller.currentUrl();
+
+      verify(mockWebView.getUrl()).called(1);
+    });
+
+    test('canGoBack', () async {
+      final MockWebView mockWebView = MockWebView();
+      final AndroidWebViewController controller = createControllerWithMocks(
+        mockWebView: mockWebView,
+      );
+
+      await controller.canGoBack();
+
+      verify(mockWebView.canGoBack()).called(1);
+    });
+
+    test('canGoForward', () async {
+      final MockWebView mockWebView = MockWebView();
+      final AndroidWebViewController controller = createControllerWithMocks(
+        mockWebView: mockWebView,
+      );
+
+      await controller.canGoForward();
+
+      verify(mockWebView.canGoForward()).called(1);
+    });
+
+    test('goBack', () async {
+      final MockWebView mockWebView = MockWebView();
+      final AndroidWebViewController controller = createControllerWithMocks(
+        mockWebView: mockWebView,
+      );
+
+      await controller.goBack();
+
+      verify(mockWebView.goBack()).called(1);
+    });
+
+    test('goForward', () async {
+      final MockWebView mockWebView = MockWebView();
+      final AndroidWebViewController controller = createControllerWithMocks(
+        mockWebView: mockWebView,
+      );
+
+      await controller.goForward();
+
+      verify(mockWebView.goForward()).called(1);
+    });
+
+    test('reload', () async {
+      final MockWebView mockWebView = MockWebView();
+      final AndroidWebViewController controller = createControllerWithMocks(
+        mockWebView: mockWebView,
+      );
+
+      await controller.reload();
+
+      verify(mockWebView.reload()).called(1);
+    });
+
+    test('clearCache', () async {
+      final MockWebView mockWebView = MockWebView();
+      final AndroidWebViewController controller = createControllerWithMocks(
+        mockWebView: mockWebView,
+      );
+
+      await controller.clearCache();
+
+      verify(mockWebView.clearCache(true)).called(1);
+    });
+
+    test('clearLocalStorage', () async {
+      final MockWebStorage mockWebStorage = MockWebStorage();
+      final AndroidWebViewController controller = createControllerWithMocks(
+        mockWebStorage: mockWebStorage,
+      );
+
+      await controller.clearLocalStorage();
+
+      verify(mockWebStorage.deleteAllData()).called(1);
+    });
+
+    test('setPlatformNavigationDelegate', () async {
+      final MockAndroidNavigationDelegate mockNavigationDelegate =
+          MockAndroidNavigationDelegate();
+      final MockWebView mockWebView = MockWebView();
+      final MockWebChromeClient mockWebChromeClient = MockWebChromeClient();
+      final MockWebViewClient mockWebViewClient = MockWebViewClient();
+      final AndroidWebViewController controller = createControllerWithMocks(
+        mockWebView: mockWebView,
+      );
+
+      when(mockNavigationDelegate.androidWebChromeClient)
+          .thenReturn(mockWebChromeClient);
+      when(mockNavigationDelegate.androidWebViewClient)
+          .thenReturn(mockWebViewClient);
+
+      await controller.setPlatformNavigationDelegate(mockNavigationDelegate);
+
+      verifyInOrder(<Object>[
+        mockWebView.setWebViewClient(mockWebViewClient),
+        mockWebView.setWebChromeClient(mockWebChromeClient),
+      ]);
+    });
+
+    test('runJavaScript', () async {
+      final MockWebView mockWebView = MockWebView();
+      final AndroidWebViewController controller = createControllerWithMocks(
+        mockWebView: mockWebView,
+      );
+
+      await controller.runJavaScript('alert("This is a test.");');
+
+      verify(mockWebView.evaluateJavascript('alert("This is a test.");'))
+          .called(1);
+    });
+
+    test('runJavaScriptReturningResult with return value', () async {
+      final MockWebView mockWebView = MockWebView();
+      final AndroidWebViewController controller = createControllerWithMocks(
+        mockWebView: mockWebView,
+      );
+
+      when(mockWebView.evaluateJavascript('return "Hello" + " World!";'))
+          .thenAnswer((_) => Future<String>.value('Hello World!'));
+
+      final String message = await controller.runJavaScriptReturningResult(
+          'return "Hello" + " World!";') as String;
+
+      expect(message, 'Hello World!');
+    });
+
+    test('runJavaScriptReturningResult returning null', () async {
+      final MockWebView mockWebView = MockWebView();
+      final AndroidWebViewController controller = createControllerWithMocks(
+        mockWebView: mockWebView,
+      );
+
+      when(mockWebView.evaluateJavascript('alert("This is a test.");'))
+          .thenAnswer((_) => Future<String?>.value());
+
+      final String message = await controller
+          .runJavaScriptReturningResult('alert("This is a test.");') as String;
+
+      expect(message, '');
+    });
+
+    test('runJavaScriptReturningResult parses num', () async {
+      final MockWebView mockWebView = MockWebView();
+      final AndroidWebViewController controller = createControllerWithMocks(
+        mockWebView: mockWebView,
+      );
+
+      when(mockWebView.evaluateJavascript('alert("This is a test.");'))
+          .thenAnswer((_) => Future<String?>.value('3.14'));
+
+      final num message = await controller
+          .runJavaScriptReturningResult('alert("This is a test.");') as num;
+
+      expect(message, 3.14);
+    });
+
+    test('runJavaScriptReturningResult parses true', () async {
+      final MockWebView mockWebView = MockWebView();
+      final AndroidWebViewController controller = createControllerWithMocks(
+        mockWebView: mockWebView,
+      );
+
+      when(mockWebView.evaluateJavascript('alert("This is a test.");'))
+          .thenAnswer((_) => Future<String?>.value('true'));
+
+      final bool message = await controller
+          .runJavaScriptReturningResult('alert("This is a test.");') as bool;
+
+      expect(message, true);
+    });
+
+    test('runJavaScriptReturningResult parses false', () async {
+      final MockWebView mockWebView = MockWebView();
+      final AndroidWebViewController controller = createControllerWithMocks(
+        mockWebView: mockWebView,
+      );
+
+      when(mockWebView.evaluateJavascript('alert("This is a test.");'))
+          .thenAnswer((_) => Future<String?>.value('false'));
+
+      final bool message = await controller
+          .runJavaScriptReturningResult('alert("This is a test.");') as bool;
+
+      expect(message, false);
+    });
+
+    test('addJavaScriptChannel', () async {
+      final MockWebView mockWebView = MockWebView();
+      final AndroidWebViewController controller = createControllerWithMocks(
+        mockWebView: mockWebView,
+      );
+      final AndroidJavaScriptChannelParams paramsWithMock =
+          createAndroidJavaScriptChannelParamsWithMocks(name: 'test');
+      await controller.addJavaScriptChannel(paramsWithMock);
+      verify(mockWebView.addJavaScriptChannel(
+              argThat(isA<android_webview.JavaScriptChannel>())))
+          .called(1);
+    });
+
+    test(
+        'addJavaScriptChannel add channel with same name should remove existing channel',
+        () async {
+      final MockWebView mockWebView = MockWebView();
+      final AndroidWebViewController controller = createControllerWithMocks(
+        mockWebView: mockWebView,
+      );
+      final AndroidJavaScriptChannelParams paramsWithMock =
+          createAndroidJavaScriptChannelParamsWithMocks(name: 'test');
+      await controller.addJavaScriptChannel(paramsWithMock);
+      verify(mockWebView.addJavaScriptChannel(
+              argThat(isA<android_webview.JavaScriptChannel>())))
+          .called(1);
+
+      await controller.addJavaScriptChannel(paramsWithMock);
+      verifyInOrder(<Object>[
+        mockWebView.removeJavaScriptChannel(
+            argThat(isA<android_webview.JavaScriptChannel>())),
+        mockWebView.addJavaScriptChannel(
+            argThat(isA<android_webview.JavaScriptChannel>())),
+      ]);
+    });
+
+    test('removeJavaScriptChannel when channel is not registered', () async {
+      final MockWebView mockWebView = MockWebView();
+      final AndroidWebViewController controller = createControllerWithMocks(
+        mockWebView: mockWebView,
+      );
+
+      await controller.removeJavaScriptChannel('test');
+      verifyNever(mockWebView.removeJavaScriptChannel(any));
+    });
+
+    test('removeJavaScriptChannel when channel exists', () async {
+      final MockWebView mockWebView = MockWebView();
+      final AndroidWebViewController controller = createControllerWithMocks(
+        mockWebView: mockWebView,
+      );
+      final AndroidJavaScriptChannelParams paramsWithMock =
+          createAndroidJavaScriptChannelParamsWithMocks(name: 'test');
+
+      // Make sure channel exists before removing it.
+      await controller.addJavaScriptChannel(paramsWithMock);
+      verify(mockWebView.addJavaScriptChannel(
+              argThat(isA<android_webview.JavaScriptChannel>())))
+          .called(1);
+
+      await controller.removeJavaScriptChannel('test');
+      verify(mockWebView.removeJavaScriptChannel(
+              argThat(isA<android_webview.JavaScriptChannel>())))
+          .called(1);
+    });
+
+    test('getTitle', () async {
+      final MockWebView mockWebView = MockWebView();
+      final AndroidWebViewController controller = createControllerWithMocks(
+        mockWebView: mockWebView,
+      );
+
+      await controller.getTitle();
+
+      verify(mockWebView.getTitle()).called(1);
+    });
+
+    test('scrollTo', () async {
+      final MockWebView mockWebView = MockWebView();
+      final AndroidWebViewController controller = createControllerWithMocks(
+        mockWebView: mockWebView,
+      );
+
+      await controller.scrollTo(4, 2);
+
+      verify(mockWebView.scrollTo(4, 2)).called(1);
+    });
+
+    test('scrollBy', () async {
+      final MockWebView mockWebView = MockWebView();
+      final AndroidWebViewController controller = createControllerWithMocks(
+        mockWebView: mockWebView,
+      );
+
+      await controller.scrollBy(4, 2);
+
+      verify(mockWebView.scrollBy(4, 2)).called(1);
+    });
+
+    test('getScrollPosition', () async {
+      final MockWebView mockWebView = MockWebView();
+      final AndroidWebViewController controller = createControllerWithMocks(
+        mockWebView: mockWebView,
+      );
+      when(mockWebView.getScrollPosition())
+          .thenAnswer((_) => Future<Offset>.value(const Offset(4, 2)));
+
+      final Offset position = await controller.getScrollPosition();
+
+      verify(mockWebView.getScrollPosition()).called(1);
+      expect(position.dx, 4);
+      expect(position.dy, 2);
+    });
+
+    test('enableDebugging', () async {
+      final MockAndroidWebViewProxy mockProxy = MockAndroidWebViewProxy();
+
+      await AndroidWebViewController.enableDebugging(
+        true,
+        webViewProxy: mockProxy,
+      );
+      verify(mockProxy.setWebContentsDebuggingEnabled(true)).called(1);
+    });
+
+    test('enableZoom', () async {
+      final MockWebView mockWebView = MockWebView();
+      final MockWebSettings mockSettings = MockWebSettings();
+      final AndroidWebViewController controller = createControllerWithMocks(
+        mockWebView: mockWebView,
+        mockSettings: mockSettings,
+      );
+
+      clearInteractions(mockWebView);
+
+      await controller.enableZoom(true);
+
+      verify(mockWebView.settings).called(1);
+      verify(mockSettings.setSupportZoom(true)).called(1);
+    });
+
+    test('setBackgroundColor', () async {
+      final MockWebView mockWebView = MockWebView();
+      final AndroidWebViewController controller = createControllerWithMocks(
+        mockWebView: mockWebView,
+      );
+
+      await controller.setBackgroundColor(Colors.blue);
+
+      verify(mockWebView.setBackgroundColor(Colors.blue)).called(1);
+    });
+
+    test('setJavaScriptMode', () async {
+      final MockWebView mockWebView = MockWebView();
+      final MockWebSettings mockSettings = MockWebSettings();
+      final AndroidWebViewController controller = createControllerWithMocks(
+        mockWebView: mockWebView,
+        mockSettings: mockSettings,
+      );
+
+      clearInteractions(mockWebView);
+
+      await controller.setJavaScriptMode(JavaScriptMode.disabled);
+
+      verify(mockWebView.settings).called(1);
+      verify(mockSettings.setJavaScriptEnabled(false)).called(1);
+    });
+
+    test('setUserAgent', () async {
+      final MockWebView mockWebView = MockWebView();
+      final MockWebSettings mockSettings = MockWebSettings();
+      final AndroidWebViewController controller = createControllerWithMocks(
+        mockWebView: mockWebView,
+        mockSettings: mockSettings,
+      );
+
+      clearInteractions(mockWebView);
+
+      await controller.setUserAgent('Test Framework');
+
+      verify(mockWebView.settings).called(1);
+      verify(mockSettings.setUserAgentString('Test Framework')).called(1);
+    });
+  });
+
+  test('setMediaPlaybackRequiresUserGesture', () async {
+    final MockWebView mockWebView = MockWebView();
+    final MockWebSettings mockSettings = MockWebSettings();
+    final AndroidWebViewController controller = createControllerWithMocks(
+      mockWebView: mockWebView,
+      mockSettings: mockSettings,
+    );
+
+    await controller.setMediaPlaybackRequiresUserGesture(true);
+
+    verify(mockSettings.setMediaPlaybackRequiresUserGesture(true)).called(1);
+  });
+
+  group('AndroidWebViewWidget', () {
+    testWidgets('Builds AndroidView using supplied parameters',
+        (WidgetTester tester) async {
+      final MockAndroidWebViewWidgetCreationParams mockParams =
+          MockAndroidWebViewWidgetCreationParams();
+      final MockInstanceManager mockInstanceManager = MockInstanceManager();
+      final MockWebView mockWebView = MockWebView();
+      final AndroidWebViewController controller =
+          createControllerWithMocks(mockWebView: mockWebView);
+
+      when(mockParams.key).thenReturn(const Key('test_web_view'));
+      when(mockParams.instanceManager).thenReturn(mockInstanceManager);
+      when(mockParams.controller).thenReturn(controller);
+      when(mockInstanceManager.getIdentifier(mockWebView)).thenReturn(42);
+
+      final AndroidWebViewWidget webViewWidget =
+          AndroidWebViewWidget(mockParams);
+
+      await tester.pumpWidget(Builder(
+        builder: (BuildContext context) => webViewWidget.build(context),
+      ));
+
+      expect(find.byType(PlatformViewLink), findsOneWidget);
+      expect(find.byKey(const Key('test_web_view')), findsOneWidget);
+    });
+  });
+}
diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_controller_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_controller_test.mocks.dart
new file mode 100644
index 0000000..d6147a5
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_controller_test.mocks.dart
@@ -0,0 +1,1679 @@
+// Mocks generated by Mockito 5.3.2 from annotations
+// in webview_flutter_android/test/android_webview_controller_test.dart.
+// Do not manually edit this file.
+
+// ignore_for_file: no_leading_underscores_for_library_prefixes
+import 'dart:async' as _i7;
+import 'dart:typed_data' as _i12;
+import 'dart:ui' as _i4;
+
+import 'package:flutter/foundation.dart' as _i10;
+import 'package:flutter/gestures.dart' as _i11;
+import 'package:mockito/mockito.dart' as _i1;
+import 'package:webview_flutter_android/src/android_navigation_delegate.dart'
+    as _i6;
+import 'package:webview_flutter_android/src/android_proxy.dart' as _i9;
+import 'package:webview_flutter_android/src/android_webview.dart' as _i2;
+import 'package:webview_flutter_android/src/android_webview_controller.dart'
+    as _i8;
+import 'package:webview_flutter_android/src/instance_manager.dart' as _i5;
+import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'
+    as _i3;
+
+// ignore_for_file: type=lint
+// ignore_for_file: avoid_redundant_argument_values
+// ignore_for_file: avoid_setters_without_getters
+// ignore_for_file: comment_references
+// ignore_for_file: implementation_imports
+// ignore_for_file: invalid_use_of_visible_for_testing_member
+// ignore_for_file: prefer_const_constructors
+// ignore_for_file: unnecessary_parenthesis
+// ignore_for_file: camel_case_types
+// ignore_for_file: subtype_of_sealed_class
+
+class _FakeWebChromeClient_0 extends _i1.SmartFake
+    implements _i2.WebChromeClient {
+  _FakeWebChromeClient_0(
+    Object parent,
+    Invocation parentInvocation,
+  ) : super(
+          parent,
+          parentInvocation,
+        );
+}
+
+class _FakeWebViewClient_1 extends _i1.SmartFake implements _i2.WebViewClient {
+  _FakeWebViewClient_1(
+    Object parent,
+    Invocation parentInvocation,
+  ) : super(
+          parent,
+          parentInvocation,
+        );
+}
+
+class _FakeDownloadListener_2 extends _i1.SmartFake
+    implements _i2.DownloadListener {
+  _FakeDownloadListener_2(
+    Object parent,
+    Invocation parentInvocation,
+  ) : super(
+          parent,
+          parentInvocation,
+        );
+}
+
+class _FakePlatformNavigationDelegateCreationParams_3 extends _i1.SmartFake
+    implements _i3.PlatformNavigationDelegateCreationParams {
+  _FakePlatformNavigationDelegateCreationParams_3(
+    Object parent,
+    Invocation parentInvocation,
+  ) : super(
+          parent,
+          parentInvocation,
+        );
+}
+
+class _FakePlatformWebViewControllerCreationParams_4 extends _i1.SmartFake
+    implements _i3.PlatformWebViewControllerCreationParams {
+  _FakePlatformWebViewControllerCreationParams_4(
+    Object parent,
+    Invocation parentInvocation,
+  ) : super(
+          parent,
+          parentInvocation,
+        );
+}
+
+class _FakeObject_5 extends _i1.SmartFake implements Object {
+  _FakeObject_5(
+    Object parent,
+    Invocation parentInvocation,
+  ) : super(
+          parent,
+          parentInvocation,
+        );
+}
+
+class _FakeOffset_6 extends _i1.SmartFake implements _i4.Offset {
+  _FakeOffset_6(
+    Object parent,
+    Invocation parentInvocation,
+  ) : super(
+          parent,
+          parentInvocation,
+        );
+}
+
+class _FakeWebView_7 extends _i1.SmartFake implements _i2.WebView {
+  _FakeWebView_7(
+    Object parent,
+    Invocation parentInvocation,
+  ) : super(
+          parent,
+          parentInvocation,
+        );
+}
+
+class _FakeFlutterAssetManager_8 extends _i1.SmartFake
+    implements _i2.FlutterAssetManager {
+  _FakeFlutterAssetManager_8(
+    Object parent,
+    Invocation parentInvocation,
+  ) : super(
+          parent,
+          parentInvocation,
+        );
+}
+
+class _FakeJavaScriptChannel_9 extends _i1.SmartFake
+    implements _i2.JavaScriptChannel {
+  _FakeJavaScriptChannel_9(
+    Object parent,
+    Invocation parentInvocation,
+  ) : super(
+          parent,
+          parentInvocation,
+        );
+}
+
+class _FakeInstanceManager_10 extends _i1.SmartFake
+    implements _i5.InstanceManager {
+  _FakeInstanceManager_10(
+    Object parent,
+    Invocation parentInvocation,
+  ) : super(
+          parent,
+          parentInvocation,
+        );
+}
+
+class _FakePlatformWebViewController_11 extends _i1.SmartFake
+    implements _i3.PlatformWebViewController {
+  _FakePlatformWebViewController_11(
+    Object parent,
+    Invocation parentInvocation,
+  ) : super(
+          parent,
+          parentInvocation,
+        );
+}
+
+class _FakeWebSettings_12 extends _i1.SmartFake implements _i2.WebSettings {
+  _FakeWebSettings_12(
+    Object parent,
+    Invocation parentInvocation,
+  ) : super(
+          parent,
+          parentInvocation,
+        );
+}
+
+class _FakeWebStorage_13 extends _i1.SmartFake implements _i2.WebStorage {
+  _FakeWebStorage_13(
+    Object parent,
+    Invocation parentInvocation,
+  ) : super(
+          parent,
+          parentInvocation,
+        );
+}
+
+/// A class which mocks [AndroidNavigationDelegate].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockAndroidNavigationDelegate extends _i1.Mock
+    implements _i6.AndroidNavigationDelegate {
+  @override
+  _i2.WebChromeClient get androidWebChromeClient => (super.noSuchMethod(
+        Invocation.getter(#androidWebChromeClient),
+        returnValue: _FakeWebChromeClient_0(
+          this,
+          Invocation.getter(#androidWebChromeClient),
+        ),
+        returnValueForMissingStub: _FakeWebChromeClient_0(
+          this,
+          Invocation.getter(#androidWebChromeClient),
+        ),
+      ) as _i2.WebChromeClient);
+  @override
+  _i2.WebViewClient get androidWebViewClient => (super.noSuchMethod(
+        Invocation.getter(#androidWebViewClient),
+        returnValue: _FakeWebViewClient_1(
+          this,
+          Invocation.getter(#androidWebViewClient),
+        ),
+        returnValueForMissingStub: _FakeWebViewClient_1(
+          this,
+          Invocation.getter(#androidWebViewClient),
+        ),
+      ) as _i2.WebViewClient);
+  @override
+  _i2.DownloadListener get androidDownloadListener => (super.noSuchMethod(
+        Invocation.getter(#androidDownloadListener),
+        returnValue: _FakeDownloadListener_2(
+          this,
+          Invocation.getter(#androidDownloadListener),
+        ),
+        returnValueForMissingStub: _FakeDownloadListener_2(
+          this,
+          Invocation.getter(#androidDownloadListener),
+        ),
+      ) as _i2.DownloadListener);
+  @override
+  _i3.PlatformNavigationDelegateCreationParams get params =>
+      (super.noSuchMethod(
+        Invocation.getter(#params),
+        returnValue: _FakePlatformNavigationDelegateCreationParams_3(
+          this,
+          Invocation.getter(#params),
+        ),
+        returnValueForMissingStub:
+            _FakePlatformNavigationDelegateCreationParams_3(
+          this,
+          Invocation.getter(#params),
+        ),
+      ) as _i3.PlatformNavigationDelegateCreationParams);
+  @override
+  _i7.Future<void> setOnLoadRequest(_i6.LoadRequestCallback? onLoadRequest) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #setOnLoadRequest,
+          [onLoadRequest],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> setOnNavigationRequest(
+          _i3.NavigationRequestCallback? onNavigationRequest) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #setOnNavigationRequest,
+          [onNavigationRequest],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> setOnPageStarted(_i3.PageEventCallback? onPageStarted) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #setOnPageStarted,
+          [onPageStarted],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> setOnPageFinished(_i3.PageEventCallback? onPageFinished) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #setOnPageFinished,
+          [onPageFinished],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> setOnProgress(_i3.ProgressCallback? onProgress) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #setOnProgress,
+          [onProgress],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> setOnWebResourceError(
+          _i3.WebResourceErrorCallback? onWebResourceError) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #setOnWebResourceError,
+          [onWebResourceError],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+}
+
+/// A class which mocks [AndroidWebViewController].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockAndroidWebViewController extends _i1.Mock
+    implements _i8.AndroidWebViewController {
+  @override
+  _i3.PlatformWebViewControllerCreationParams get params => (super.noSuchMethod(
+        Invocation.getter(#params),
+        returnValue: _FakePlatformWebViewControllerCreationParams_4(
+          this,
+          Invocation.getter(#params),
+        ),
+        returnValueForMissingStub:
+            _FakePlatformWebViewControllerCreationParams_4(
+          this,
+          Invocation.getter(#params),
+        ),
+      ) as _i3.PlatformWebViewControllerCreationParams);
+  @override
+  _i7.Future<void> loadFile(String? absoluteFilePath) => (super.noSuchMethod(
+        Invocation.method(
+          #loadFile,
+          [absoluteFilePath],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> loadFlutterAsset(String? key) => (super.noSuchMethod(
+        Invocation.method(
+          #loadFlutterAsset,
+          [key],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> loadHtmlString(
+    String? html, {
+    String? baseUrl,
+  }) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #loadHtmlString,
+          [html],
+          {#baseUrl: baseUrl},
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> loadRequest(_i3.LoadRequestParams? params) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #loadRequest,
+          [params],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<String?> currentUrl() => (super.noSuchMethod(
+        Invocation.method(
+          #currentUrl,
+          [],
+        ),
+        returnValue: _i7.Future<String?>.value(),
+        returnValueForMissingStub: _i7.Future<String?>.value(),
+      ) as _i7.Future<String?>);
+  @override
+  _i7.Future<bool> canGoBack() => (super.noSuchMethod(
+        Invocation.method(
+          #canGoBack,
+          [],
+        ),
+        returnValue: _i7.Future<bool>.value(false),
+        returnValueForMissingStub: _i7.Future<bool>.value(false),
+      ) as _i7.Future<bool>);
+  @override
+  _i7.Future<bool> canGoForward() => (super.noSuchMethod(
+        Invocation.method(
+          #canGoForward,
+          [],
+        ),
+        returnValue: _i7.Future<bool>.value(false),
+        returnValueForMissingStub: _i7.Future<bool>.value(false),
+      ) as _i7.Future<bool>);
+  @override
+  _i7.Future<void> goBack() => (super.noSuchMethod(
+        Invocation.method(
+          #goBack,
+          [],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> goForward() => (super.noSuchMethod(
+        Invocation.method(
+          #goForward,
+          [],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> reload() => (super.noSuchMethod(
+        Invocation.method(
+          #reload,
+          [],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> clearCache() => (super.noSuchMethod(
+        Invocation.method(
+          #clearCache,
+          [],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> clearLocalStorage() => (super.noSuchMethod(
+        Invocation.method(
+          #clearLocalStorage,
+          [],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> setPlatformNavigationDelegate(
+          _i3.PlatformNavigationDelegate? handler) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #setPlatformNavigationDelegate,
+          [handler],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> runJavaScript(String? javaScript) => (super.noSuchMethod(
+        Invocation.method(
+          #runJavaScript,
+          [javaScript],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<Object> runJavaScriptReturningResult(String? javaScript) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #runJavaScriptReturningResult,
+          [javaScript],
+        ),
+        returnValue: _i7.Future<Object>.value(_FakeObject_5(
+          this,
+          Invocation.method(
+            #runJavaScriptReturningResult,
+            [javaScript],
+          ),
+        )),
+        returnValueForMissingStub: _i7.Future<Object>.value(_FakeObject_5(
+          this,
+          Invocation.method(
+            #runJavaScriptReturningResult,
+            [javaScript],
+          ),
+        )),
+      ) as _i7.Future<Object>);
+  @override
+  _i7.Future<void> addJavaScriptChannel(
+          _i3.JavaScriptChannelParams? javaScriptChannelParams) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #addJavaScriptChannel,
+          [javaScriptChannelParams],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> removeJavaScriptChannel(String? javaScriptChannelName) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #removeJavaScriptChannel,
+          [javaScriptChannelName],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<String?> getTitle() => (super.noSuchMethod(
+        Invocation.method(
+          #getTitle,
+          [],
+        ),
+        returnValue: _i7.Future<String?>.value(),
+        returnValueForMissingStub: _i7.Future<String?>.value(),
+      ) as _i7.Future<String?>);
+  @override
+  _i7.Future<void> scrollTo(
+    int? x,
+    int? y,
+  ) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #scrollTo,
+          [
+            x,
+            y,
+          ],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> scrollBy(
+    int? x,
+    int? y,
+  ) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #scrollBy,
+          [
+            x,
+            y,
+          ],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<_i4.Offset> getScrollPosition() => (super.noSuchMethod(
+        Invocation.method(
+          #getScrollPosition,
+          [],
+        ),
+        returnValue: _i7.Future<_i4.Offset>.value(_FakeOffset_6(
+          this,
+          Invocation.method(
+            #getScrollPosition,
+            [],
+          ),
+        )),
+        returnValueForMissingStub: _i7.Future<_i4.Offset>.value(_FakeOffset_6(
+          this,
+          Invocation.method(
+            #getScrollPosition,
+            [],
+          ),
+        )),
+      ) as _i7.Future<_i4.Offset>);
+  @override
+  _i7.Future<void> enableZoom(bool? enabled) => (super.noSuchMethod(
+        Invocation.method(
+          #enableZoom,
+          [enabled],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> setBackgroundColor(_i4.Color? color) => (super.noSuchMethod(
+        Invocation.method(
+          #setBackgroundColor,
+          [color],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> setJavaScriptMode(_i3.JavaScriptMode? javaScriptMode) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #setJavaScriptMode,
+          [javaScriptMode],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> setUserAgent(String? userAgent) => (super.noSuchMethod(
+        Invocation.method(
+          #setUserAgent,
+          [userAgent],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> setMediaPlaybackRequiresUserGesture(bool? require) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #setMediaPlaybackRequiresUserGesture,
+          [require],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+}
+
+/// A class which mocks [AndroidWebViewProxy].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockAndroidWebViewProxy extends _i1.Mock
+    implements _i9.AndroidWebViewProxy {
+  @override
+  _i2.WebView Function({required bool useHybridComposition})
+      get createAndroidWebView => (super.noSuchMethod(
+            Invocation.getter(#createAndroidWebView),
+            returnValue: ({required bool useHybridComposition}) =>
+                _FakeWebView_7(
+              this,
+              Invocation.getter(#createAndroidWebView),
+            ),
+            returnValueForMissingStub: ({required bool useHybridComposition}) =>
+                _FakeWebView_7(
+              this,
+              Invocation.getter(#createAndroidWebView),
+            ),
+          ) as _i2.WebView Function({required bool useHybridComposition}));
+  @override
+  _i2.WebChromeClient Function(
+      {void Function(
+        _i2.WebView,
+        int,
+      )?
+          onProgressChanged}) get createAndroidWebChromeClient =>
+      (super.noSuchMethod(
+        Invocation.getter(#createAndroidWebChromeClient),
+        returnValue: (
+                {void Function(
+                  _i2.WebView,
+                  int,
+                )?
+                    onProgressChanged}) =>
+            _FakeWebChromeClient_0(
+          this,
+          Invocation.getter(#createAndroidWebChromeClient),
+        ),
+        returnValueForMissingStub: (
+                {void Function(
+                  _i2.WebView,
+                  int,
+                )?
+                    onProgressChanged}) =>
+            _FakeWebChromeClient_0(
+          this,
+          Invocation.getter(#createAndroidWebChromeClient),
+        ),
+      ) as _i2.WebChromeClient Function(
+          {void Function(
+            _i2.WebView,
+            int,
+          )?
+              onProgressChanged}));
+  @override
+  _i2.WebViewClient Function({
+    void Function(
+      _i2.WebView,
+      String,
+    )?
+        onPageFinished,
+    void Function(
+      _i2.WebView,
+      String,
+    )?
+        onPageStarted,
+    void Function(
+      _i2.WebView,
+      int,
+      String,
+      String,
+    )?
+        onReceivedError,
+    void Function(
+      _i2.WebView,
+      _i2.WebResourceRequest,
+      _i2.WebResourceError,
+    )?
+        onReceivedRequestError,
+    void Function(
+      _i2.WebView,
+      _i2.WebResourceRequest,
+    )?
+        requestLoading,
+    void Function(
+      _i2.WebView,
+      String,
+    )?
+        urlLoading,
+  }) get createAndroidWebViewClient => (super.noSuchMethod(
+        Invocation.getter(#createAndroidWebViewClient),
+        returnValue: ({
+          void Function(
+            _i2.WebView,
+            String,
+          )?
+              onPageFinished,
+          void Function(
+            _i2.WebView,
+            String,
+          )?
+              onPageStarted,
+          void Function(
+            _i2.WebView,
+            int,
+            String,
+            String,
+          )?
+              onReceivedError,
+          void Function(
+            _i2.WebView,
+            _i2.WebResourceRequest,
+            _i2.WebResourceError,
+          )?
+              onReceivedRequestError,
+          void Function(
+            _i2.WebView,
+            _i2.WebResourceRequest,
+          )?
+              requestLoading,
+          void Function(
+            _i2.WebView,
+            String,
+          )?
+              urlLoading,
+        }) =>
+            _FakeWebViewClient_1(
+          this,
+          Invocation.getter(#createAndroidWebViewClient),
+        ),
+        returnValueForMissingStub: ({
+          void Function(
+            _i2.WebView,
+            String,
+          )?
+              onPageFinished,
+          void Function(
+            _i2.WebView,
+            String,
+          )?
+              onPageStarted,
+          void Function(
+            _i2.WebView,
+            int,
+            String,
+            String,
+          )?
+              onReceivedError,
+          void Function(
+            _i2.WebView,
+            _i2.WebResourceRequest,
+            _i2.WebResourceError,
+          )?
+              onReceivedRequestError,
+          void Function(
+            _i2.WebView,
+            _i2.WebResourceRequest,
+          )?
+              requestLoading,
+          void Function(
+            _i2.WebView,
+            String,
+          )?
+              urlLoading,
+        }) =>
+            _FakeWebViewClient_1(
+          this,
+          Invocation.getter(#createAndroidWebViewClient),
+        ),
+      ) as _i2.WebViewClient Function({
+        void Function(
+          _i2.WebView,
+          String,
+        )?
+            onPageFinished,
+        void Function(
+          _i2.WebView,
+          String,
+        )?
+            onPageStarted,
+        void Function(
+          _i2.WebView,
+          int,
+          String,
+          String,
+        )?
+            onReceivedError,
+        void Function(
+          _i2.WebView,
+          _i2.WebResourceRequest,
+          _i2.WebResourceError,
+        )?
+            onReceivedRequestError,
+        void Function(
+          _i2.WebView,
+          _i2.WebResourceRequest,
+        )?
+            requestLoading,
+        void Function(
+          _i2.WebView,
+          String,
+        )?
+            urlLoading,
+      }));
+  @override
+  _i2.FlutterAssetManager Function() get createFlutterAssetManager =>
+      (super.noSuchMethod(
+        Invocation.getter(#createFlutterAssetManager),
+        returnValue: () => _FakeFlutterAssetManager_8(
+          this,
+          Invocation.getter(#createFlutterAssetManager),
+        ),
+        returnValueForMissingStub: () => _FakeFlutterAssetManager_8(
+          this,
+          Invocation.getter(#createFlutterAssetManager),
+        ),
+      ) as _i2.FlutterAssetManager Function());
+  @override
+  _i2.JavaScriptChannel Function(
+    String, {
+    required void Function(String) postMessage,
+  }) get createJavaScriptChannel => (super.noSuchMethod(
+        Invocation.getter(#createJavaScriptChannel),
+        returnValue: (
+          String channelName, {
+          required void Function(String) postMessage,
+        }) =>
+            _FakeJavaScriptChannel_9(
+          this,
+          Invocation.getter(#createJavaScriptChannel),
+        ),
+        returnValueForMissingStub: (
+          String channelName, {
+          required void Function(String) postMessage,
+        }) =>
+            _FakeJavaScriptChannel_9(
+          this,
+          Invocation.getter(#createJavaScriptChannel),
+        ),
+      ) as _i2.JavaScriptChannel Function(
+        String, {
+        required void Function(String) postMessage,
+      }));
+  @override
+  _i2.DownloadListener Function(
+      {required void Function(
+        String,
+        String,
+        String,
+        String,
+        int,
+      )
+          onDownloadStart}) get createDownloadListener => (super.noSuchMethod(
+        Invocation.getter(#createDownloadListener),
+        returnValue: (
+                {required void Function(
+                  String,
+                  String,
+                  String,
+                  String,
+                  int,
+                )
+                    onDownloadStart}) =>
+            _FakeDownloadListener_2(
+          this,
+          Invocation.getter(#createDownloadListener),
+        ),
+        returnValueForMissingStub: (
+                {required void Function(
+                  String,
+                  String,
+                  String,
+                  String,
+                  int,
+                )
+                    onDownloadStart}) =>
+            _FakeDownloadListener_2(
+          this,
+          Invocation.getter(#createDownloadListener),
+        ),
+      ) as _i2.DownloadListener Function(
+          {required void Function(
+            String,
+            String,
+            String,
+            String,
+            int,
+          )
+              onDownloadStart}));
+  @override
+  _i7.Future<void> setWebContentsDebuggingEnabled(bool? enabled) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #setWebContentsDebuggingEnabled,
+          [enabled],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+}
+
+/// A class which mocks [AndroidWebViewWidgetCreationParams].
+///
+/// See the documentation for Mockito's code generation for more information.
+// ignore: must_be_immutable
+class MockAndroidWebViewWidgetCreationParams extends _i1.Mock
+    implements _i8.AndroidWebViewWidgetCreationParams {
+  @override
+  _i5.InstanceManager get instanceManager => (super.noSuchMethod(
+        Invocation.getter(#instanceManager),
+        returnValue: _FakeInstanceManager_10(
+          this,
+          Invocation.getter(#instanceManager),
+        ),
+        returnValueForMissingStub: _FakeInstanceManager_10(
+          this,
+          Invocation.getter(#instanceManager),
+        ),
+      ) as _i5.InstanceManager);
+  @override
+  _i3.PlatformWebViewController get controller => (super.noSuchMethod(
+        Invocation.getter(#controller),
+        returnValue: _FakePlatformWebViewController_11(
+          this,
+          Invocation.getter(#controller),
+        ),
+        returnValueForMissingStub: _FakePlatformWebViewController_11(
+          this,
+          Invocation.getter(#controller),
+        ),
+      ) as _i3.PlatformWebViewController);
+  @override
+  _i4.TextDirection get layoutDirection => (super.noSuchMethod(
+        Invocation.getter(#layoutDirection),
+        returnValue: _i4.TextDirection.rtl,
+        returnValueForMissingStub: _i4.TextDirection.rtl,
+      ) as _i4.TextDirection);
+  @override
+  Set<_i10.Factory<_i11.OneSequenceGestureRecognizer>> get gestureRecognizers =>
+      (super.noSuchMethod(
+        Invocation.getter(#gestureRecognizers),
+        returnValue: <_i10.Factory<_i11.OneSequenceGestureRecognizer>>{},
+        returnValueForMissingStub: <
+            _i10.Factory<_i11.OneSequenceGestureRecognizer>>{},
+      ) as Set<_i10.Factory<_i11.OneSequenceGestureRecognizer>>);
+}
+
+/// A class which mocks [FlutterAssetManager].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockFlutterAssetManager extends _i1.Mock
+    implements _i2.FlutterAssetManager {
+  @override
+  _i7.Future<List<String?>> list(String? path) => (super.noSuchMethod(
+        Invocation.method(
+          #list,
+          [path],
+        ),
+        returnValue: _i7.Future<List<String?>>.value(<String?>[]),
+        returnValueForMissingStub: _i7.Future<List<String?>>.value(<String?>[]),
+      ) as _i7.Future<List<String?>>);
+  @override
+  _i7.Future<String> getAssetFilePathByName(String? name) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #getAssetFilePathByName,
+          [name],
+        ),
+        returnValue: _i7.Future<String>.value(''),
+        returnValueForMissingStub: _i7.Future<String>.value(''),
+      ) as _i7.Future<String>);
+}
+
+/// A class which mocks [JavaScriptChannel].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockJavaScriptChannel extends _i1.Mock implements _i2.JavaScriptChannel {
+  @override
+  String get channelName => (super.noSuchMethod(
+        Invocation.getter(#channelName),
+        returnValue: '',
+        returnValueForMissingStub: '',
+      ) as String);
+  @override
+  void Function(String) get postMessage => (super.noSuchMethod(
+        Invocation.getter(#postMessage),
+        returnValue: (String message) {},
+        returnValueForMissingStub: (String message) {},
+      ) as void Function(String));
+  @override
+  _i2.JavaScriptChannel copy() => (super.noSuchMethod(
+        Invocation.method(
+          #copy,
+          [],
+        ),
+        returnValue: _FakeJavaScriptChannel_9(
+          this,
+          Invocation.method(
+            #copy,
+            [],
+          ),
+        ),
+        returnValueForMissingStub: _FakeJavaScriptChannel_9(
+          this,
+          Invocation.method(
+            #copy,
+            [],
+          ),
+        ),
+      ) as _i2.JavaScriptChannel);
+}
+
+/// A class which mocks [WebChromeClient].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockWebChromeClient extends _i1.Mock implements _i2.WebChromeClient {
+  @override
+  _i2.WebChromeClient copy() => (super.noSuchMethod(
+        Invocation.method(
+          #copy,
+          [],
+        ),
+        returnValue: _FakeWebChromeClient_0(
+          this,
+          Invocation.method(
+            #copy,
+            [],
+          ),
+        ),
+        returnValueForMissingStub: _FakeWebChromeClient_0(
+          this,
+          Invocation.method(
+            #copy,
+            [],
+          ),
+        ),
+      ) as _i2.WebChromeClient);
+}
+
+/// A class which mocks [WebSettings].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockWebSettings extends _i1.Mock implements _i2.WebSettings {
+  @override
+  _i7.Future<void> setDomStorageEnabled(bool? flag) => (super.noSuchMethod(
+        Invocation.method(
+          #setDomStorageEnabled,
+          [flag],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> setJavaScriptCanOpenWindowsAutomatically(bool? flag) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #setJavaScriptCanOpenWindowsAutomatically,
+          [flag],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> setSupportMultipleWindows(bool? support) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #setSupportMultipleWindows,
+          [support],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> setJavaScriptEnabled(bool? flag) => (super.noSuchMethod(
+        Invocation.method(
+          #setJavaScriptEnabled,
+          [flag],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> setUserAgentString(String? userAgentString) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #setUserAgentString,
+          [userAgentString],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> setMediaPlaybackRequiresUserGesture(bool? require) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #setMediaPlaybackRequiresUserGesture,
+          [require],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> setSupportZoom(bool? support) => (super.noSuchMethod(
+        Invocation.method(
+          #setSupportZoom,
+          [support],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> setLoadWithOverviewMode(bool? overview) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #setLoadWithOverviewMode,
+          [overview],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> setUseWideViewPort(bool? use) => (super.noSuchMethod(
+        Invocation.method(
+          #setUseWideViewPort,
+          [use],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> setDisplayZoomControls(bool? enabled) => (super.noSuchMethod(
+        Invocation.method(
+          #setDisplayZoomControls,
+          [enabled],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> setBuiltInZoomControls(bool? enabled) => (super.noSuchMethod(
+        Invocation.method(
+          #setBuiltInZoomControls,
+          [enabled],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> setAllowFileAccess(bool? enabled) => (super.noSuchMethod(
+        Invocation.method(
+          #setAllowFileAccess,
+          [enabled],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i2.WebSettings copy() => (super.noSuchMethod(
+        Invocation.method(
+          #copy,
+          [],
+        ),
+        returnValue: _FakeWebSettings_12(
+          this,
+          Invocation.method(
+            #copy,
+            [],
+          ),
+        ),
+        returnValueForMissingStub: _FakeWebSettings_12(
+          this,
+          Invocation.method(
+            #copy,
+            [],
+          ),
+        ),
+      ) as _i2.WebSettings);
+}
+
+/// A class which mocks [WebView].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockWebView extends _i1.Mock implements _i2.WebView {
+  @override
+  bool get useHybridComposition => (super.noSuchMethod(
+        Invocation.getter(#useHybridComposition),
+        returnValue: false,
+        returnValueForMissingStub: false,
+      ) as bool);
+  @override
+  _i2.WebSettings get settings => (super.noSuchMethod(
+        Invocation.getter(#settings),
+        returnValue: _FakeWebSettings_12(
+          this,
+          Invocation.getter(#settings),
+        ),
+        returnValueForMissingStub: _FakeWebSettings_12(
+          this,
+          Invocation.getter(#settings),
+        ),
+      ) as _i2.WebSettings);
+  @override
+  _i7.Future<void> loadData({
+    required String? data,
+    String? mimeType,
+    String? encoding,
+  }) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #loadData,
+          [],
+          {
+            #data: data,
+            #mimeType: mimeType,
+            #encoding: encoding,
+          },
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> loadDataWithBaseUrl({
+    String? baseUrl,
+    required String? data,
+    String? mimeType,
+    String? encoding,
+    String? historyUrl,
+  }) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #loadDataWithBaseUrl,
+          [],
+          {
+            #baseUrl: baseUrl,
+            #data: data,
+            #mimeType: mimeType,
+            #encoding: encoding,
+            #historyUrl: historyUrl,
+          },
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> loadUrl(
+    String? url,
+    Map<String, String>? headers,
+  ) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #loadUrl,
+          [
+            url,
+            headers,
+          ],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> postUrl(
+    String? url,
+    _i12.Uint8List? data,
+  ) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #postUrl,
+          [
+            url,
+            data,
+          ],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<String?> getUrl() => (super.noSuchMethod(
+        Invocation.method(
+          #getUrl,
+          [],
+        ),
+        returnValue: _i7.Future<String?>.value(),
+        returnValueForMissingStub: _i7.Future<String?>.value(),
+      ) as _i7.Future<String?>);
+  @override
+  _i7.Future<bool> canGoBack() => (super.noSuchMethod(
+        Invocation.method(
+          #canGoBack,
+          [],
+        ),
+        returnValue: _i7.Future<bool>.value(false),
+        returnValueForMissingStub: _i7.Future<bool>.value(false),
+      ) as _i7.Future<bool>);
+  @override
+  _i7.Future<bool> canGoForward() => (super.noSuchMethod(
+        Invocation.method(
+          #canGoForward,
+          [],
+        ),
+        returnValue: _i7.Future<bool>.value(false),
+        returnValueForMissingStub: _i7.Future<bool>.value(false),
+      ) as _i7.Future<bool>);
+  @override
+  _i7.Future<void> goBack() => (super.noSuchMethod(
+        Invocation.method(
+          #goBack,
+          [],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> goForward() => (super.noSuchMethod(
+        Invocation.method(
+          #goForward,
+          [],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> reload() => (super.noSuchMethod(
+        Invocation.method(
+          #reload,
+          [],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> clearCache(bool? includeDiskFiles) => (super.noSuchMethod(
+        Invocation.method(
+          #clearCache,
+          [includeDiskFiles],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<String?> evaluateJavascript(String? javascriptString) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #evaluateJavascript,
+          [javascriptString],
+        ),
+        returnValue: _i7.Future<String?>.value(),
+        returnValueForMissingStub: _i7.Future<String?>.value(),
+      ) as _i7.Future<String?>);
+  @override
+  _i7.Future<String?> getTitle() => (super.noSuchMethod(
+        Invocation.method(
+          #getTitle,
+          [],
+        ),
+        returnValue: _i7.Future<String?>.value(),
+        returnValueForMissingStub: _i7.Future<String?>.value(),
+      ) as _i7.Future<String?>);
+  @override
+  _i7.Future<void> scrollTo(
+    int? x,
+    int? y,
+  ) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #scrollTo,
+          [
+            x,
+            y,
+          ],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> scrollBy(
+    int? x,
+    int? y,
+  ) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #scrollBy,
+          [
+            x,
+            y,
+          ],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<int> getScrollX() => (super.noSuchMethod(
+        Invocation.method(
+          #getScrollX,
+          [],
+        ),
+        returnValue: _i7.Future<int>.value(0),
+        returnValueForMissingStub: _i7.Future<int>.value(0),
+      ) as _i7.Future<int>);
+  @override
+  _i7.Future<int> getScrollY() => (super.noSuchMethod(
+        Invocation.method(
+          #getScrollY,
+          [],
+        ),
+        returnValue: _i7.Future<int>.value(0),
+        returnValueForMissingStub: _i7.Future<int>.value(0),
+      ) as _i7.Future<int>);
+  @override
+  _i7.Future<_i4.Offset> getScrollPosition() => (super.noSuchMethod(
+        Invocation.method(
+          #getScrollPosition,
+          [],
+        ),
+        returnValue: _i7.Future<_i4.Offset>.value(_FakeOffset_6(
+          this,
+          Invocation.method(
+            #getScrollPosition,
+            [],
+          ),
+        )),
+        returnValueForMissingStub: _i7.Future<_i4.Offset>.value(_FakeOffset_6(
+          this,
+          Invocation.method(
+            #getScrollPosition,
+            [],
+          ),
+        )),
+      ) as _i7.Future<_i4.Offset>);
+  @override
+  _i7.Future<void> setWebViewClient(_i2.WebViewClient? webViewClient) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #setWebViewClient,
+          [webViewClient],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> addJavaScriptChannel(
+          _i2.JavaScriptChannel? javaScriptChannel) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #addJavaScriptChannel,
+          [javaScriptChannel],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> removeJavaScriptChannel(
+          _i2.JavaScriptChannel? javaScriptChannel) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #removeJavaScriptChannel,
+          [javaScriptChannel],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> setDownloadListener(_i2.DownloadListener? listener) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #setDownloadListener,
+          [listener],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> setWebChromeClient(_i2.WebChromeClient? client) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #setWebChromeClient,
+          [client],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i7.Future<void> setBackgroundColor(_i4.Color? color) => (super.noSuchMethod(
+        Invocation.method(
+          #setBackgroundColor,
+          [color],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i2.WebView copy() => (super.noSuchMethod(
+        Invocation.method(
+          #copy,
+          [],
+        ),
+        returnValue: _FakeWebView_7(
+          this,
+          Invocation.method(
+            #copy,
+            [],
+          ),
+        ),
+        returnValueForMissingStub: _FakeWebView_7(
+          this,
+          Invocation.method(
+            #copy,
+            [],
+          ),
+        ),
+      ) as _i2.WebView);
+}
+
+/// A class which mocks [WebViewClient].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockWebViewClient extends _i1.Mock implements _i2.WebViewClient {
+  @override
+  _i7.Future<void> setSynchronousReturnValueForShouldOverrideUrlLoading(
+          bool? value) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #setSynchronousReturnValueForShouldOverrideUrlLoading,
+          [value],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i2.WebViewClient copy() => (super.noSuchMethod(
+        Invocation.method(
+          #copy,
+          [],
+        ),
+        returnValue: _FakeWebViewClient_1(
+          this,
+          Invocation.method(
+            #copy,
+            [],
+          ),
+        ),
+        returnValueForMissingStub: _FakeWebViewClient_1(
+          this,
+          Invocation.method(
+            #copy,
+            [],
+          ),
+        ),
+      ) as _i2.WebViewClient);
+}
+
+/// A class which mocks [WebStorage].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockWebStorage extends _i1.Mock implements _i2.WebStorage {
+  @override
+  _i7.Future<void> deleteAllData() => (super.noSuchMethod(
+        Invocation.method(
+          #deleteAllData,
+          [],
+        ),
+        returnValue: _i7.Future<void>.value(),
+        returnValueForMissingStub: _i7.Future<void>.value(),
+      ) as _i7.Future<void>);
+  @override
+  _i2.WebStorage copy() => (super.noSuchMethod(
+        Invocation.method(
+          #copy,
+          [],
+        ),
+        returnValue: _FakeWebStorage_13(
+          this,
+          Invocation.method(
+            #copy,
+            [],
+          ),
+        ),
+        returnValueForMissingStub: _FakeWebStorage_13(
+          this,
+          Invocation.method(
+            #copy,
+            [],
+          ),
+        ),
+      ) as _i2.WebStorage);
+}
+
+/// A class which mocks [InstanceManager].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockInstanceManager extends _i1.Mock implements _i5.InstanceManager {
+  @override
+  void Function(int) get onWeakReferenceRemoved => (super.noSuchMethod(
+        Invocation.getter(#onWeakReferenceRemoved),
+        returnValue: (int __p0) {},
+        returnValueForMissingStub: (int __p0) {},
+      ) as void Function(int));
+  @override
+  set onWeakReferenceRemoved(void Function(int)? _onWeakReferenceRemoved) =>
+      super.noSuchMethod(
+        Invocation.setter(
+          #onWeakReferenceRemoved,
+          _onWeakReferenceRemoved,
+        ),
+        returnValueForMissingStub: null,
+      );
+  @override
+  int addDartCreatedInstance(_i5.Copyable? instance) => (super.noSuchMethod(
+        Invocation.method(
+          #addDartCreatedInstance,
+          [instance],
+        ),
+        returnValue: 0,
+        returnValueForMissingStub: 0,
+      ) as int);
+  @override
+  int? removeWeakReference(_i5.Copyable? instance) => (super.noSuchMethod(
+        Invocation.method(
+          #removeWeakReference,
+          [instance],
+        ),
+        returnValueForMissingStub: null,
+      ) as int?);
+  @override
+  T? remove<T extends _i5.Copyable>(int? identifier) => (super.noSuchMethod(
+        Invocation.method(
+          #remove,
+          [identifier],
+        ),
+        returnValueForMissingStub: null,
+      ) as T?);
+  @override
+  T? getInstanceWithWeakReference<T extends _i5.Copyable>(int? identifier) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #getInstanceWithWeakReference,
+          [identifier],
+        ),
+        returnValueForMissingStub: null,
+      ) as T?);
+  @override
+  int? getIdentifier(_i5.Copyable? instance) => (super.noSuchMethod(
+        Invocation.method(
+          #getIdentifier,
+          [instance],
+        ),
+        returnValueForMissingStub: null,
+      ) as int?);
+  @override
+  void addHostCreatedInstance(
+    _i5.Copyable? instance,
+    int? identifier,
+  ) =>
+      super.noSuchMethod(
+        Invocation.method(
+          #addHostCreatedInstance,
+          [
+            instance,
+            identifier,
+          ],
+        ),
+        returnValueForMissingStub: null,
+      );
+  @override
+  bool containsIdentifier(int? identifier) => (super.noSuchMethod(
+        Invocation.method(
+          #containsIdentifier,
+          [identifier],
+        ),
+        returnValue: false,
+        returnValueForMissingStub: false,
+      ) as bool);
+}
diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_cookie_manager_test.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_cookie_manager_test.dart
new file mode 100644
index 0000000..9e7422f
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_cookie_manager_test.dart
@@ -0,0 +1,79 @@
+// 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.
+
+import 'package:flutter_test/flutter_test.dart';
+import 'package:mockito/annotations.dart';
+import 'package:mockito/mockito.dart';
+import 'package:webview_flutter_android/src/android_webview.dart'
+    as android_webview;
+import 'package:webview_flutter_android/webview_flutter_android.dart';
+import 'package:webview_flutter_platform_interface/src/webview_platform.dart';
+
+import 'android_webview_cookie_manager_test.mocks.dart';
+
+@GenerateMocks(<Type>[android_webview.CookieManager])
+void main() {
+  TestWidgetsFlutterBinding.ensureInitialized();
+
+  test('clearCookies should call android_webview.clearCookies', () async {
+    final android_webview.CookieManager mockCookieManager = MockCookieManager();
+
+    when(mockCookieManager.clearCookies())
+        .thenAnswer((_) => Future<bool>.value(true));
+
+    final AndroidWebViewCookieManagerCreationParams params =
+        AndroidWebViewCookieManagerCreationParams
+            .fromPlatformWebViewCookieManagerCreationParams(
+                const PlatformWebViewCookieManagerCreationParams());
+
+    final bool hasClearedCookies = await AndroidWebViewCookieManager(params,
+            cookieManager: mockCookieManager)
+        .clearCookies();
+
+    expect(hasClearedCookies, true);
+    verify(mockCookieManager.clearCookies());
+  });
+
+  test('setCookie should throw ArgumentError for cookie with invalid path', () {
+    final AndroidWebViewCookieManagerCreationParams params =
+        AndroidWebViewCookieManagerCreationParams
+            .fromPlatformWebViewCookieManagerCreationParams(
+                const PlatformWebViewCookieManagerCreationParams());
+
+    final AndroidWebViewCookieManager androidCookieManager =
+        AndroidWebViewCookieManager(params, cookieManager: MockCookieManager());
+
+    expect(
+      () => androidCookieManager.setCookie(const WebViewCookie(
+        name: 'foo',
+        value: 'bar',
+        domain: 'flutter.dev',
+        path: 'invalid;path',
+      )),
+      throwsA(const TypeMatcher<ArgumentError>()),
+    );
+  });
+
+  test(
+      'setCookie should call android_webview.csetCookie with properly formatted cookie value',
+      () {
+    final android_webview.CookieManager mockCookieManager = MockCookieManager();
+    final AndroidWebViewCookieManagerCreationParams params =
+        AndroidWebViewCookieManagerCreationParams
+            .fromPlatformWebViewCookieManagerCreationParams(
+                const PlatformWebViewCookieManagerCreationParams());
+
+    AndroidWebViewCookieManager(params, cookieManager: mockCookieManager)
+        .setCookie(const WebViewCookie(
+      name: 'foo&',
+      value: 'bar@',
+      domain: 'flutter.dev',
+    ));
+
+    verify(mockCookieManager.setCookie(
+      'flutter.dev',
+      'foo%26=bar%40; path=/',
+    ));
+  });
+}
diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_cookie_manager_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_cookie_manager_test.mocks.dart
new file mode 100644
index 0000000..0732180
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_cookie_manager_test.mocks.dart
@@ -0,0 +1,54 @@
+// Mocks generated by Mockito 5.3.2 from annotations
+// in webview_flutter_android/test/android_webview_cookie_manager_test.dart.
+// Do not manually edit this file.
+
+// ignore_for_file: no_leading_underscores_for_library_prefixes
+import 'dart:async' as _i3;
+
+import 'package:mockito/mockito.dart' as _i1;
+import 'package:webview_flutter_android/src/android_webview.dart' as _i2;
+
+// ignore_for_file: type=lint
+// ignore_for_file: avoid_redundant_argument_values
+// ignore_for_file: avoid_setters_without_getters
+// ignore_for_file: comment_references
+// ignore_for_file: implementation_imports
+// ignore_for_file: invalid_use_of_visible_for_testing_member
+// ignore_for_file: prefer_const_constructors
+// ignore_for_file: unnecessary_parenthesis
+// ignore_for_file: camel_case_types
+// ignore_for_file: subtype_of_sealed_class
+
+/// A class which mocks [CookieManager].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockCookieManager extends _i1.Mock implements _i2.CookieManager {
+  MockCookieManager() {
+    _i1.throwOnMissingStub(this);
+  }
+
+  @override
+  _i3.Future<void> setCookie(
+    String? url,
+    String? value,
+  ) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #setCookie,
+          [
+            url,
+            value,
+          ],
+        ),
+        returnValue: _i3.Future<void>.value(),
+        returnValueForMissingStub: _i3.Future<void>.value(),
+      ) as _i3.Future<void>);
+  @override
+  _i3.Future<bool> clearCookies() => (super.noSuchMethod(
+        Invocation.method(
+          #clearCookies,
+          [],
+        ),
+        returnValue: _i3.Future<bool>.value(false),
+      ) as _i3.Future<bool>);
+}
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..4e972fe 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
@@ -269,7 +269,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 +334,7 @@
 
         final DownloadListener mockDownloadListener = MockDownloadListener();
         when(mockDownloadListener.copy()).thenReturn(MockDownloadListener());
+        instanceManager.addDartCreatedInstance(mockDownloadListener);
         webView.setDownloadListener(mockDownloadListener);
 
         final int downloadListenerInstanceId =
@@ -345,16 +346,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 +353,7 @@
 
         final WebChromeClient mockWebChromeClient = MockWebChromeClient();
         when(mockWebChromeClient.copy()).thenReturn(MockWebChromeClient());
+        instanceManager.addDartCreatedInstance(mockWebChromeClient);
         webView.setWebChromeClient(mockWebChromeClient);
 
         final int webChromeClientInstanceId =
@@ -372,21 +364,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 +521,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 +571,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 +630,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 +657,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 +686,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 +734,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 +762,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 +814,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..81cdc9e 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
@@ -1,7 +1,8 @@
-// Mocks generated by Mockito 5.2.0 from annotations
+// Mocks generated by Mockito 5.3.2 from annotations
 // in webview_flutter_android/test/android_webview_test.dart.
 // Do not manually edit this file.
 
+// ignore_for_file: no_leading_underscores_for_library_prefixes
 import 'dart:async' as _i5;
 import 'dart:typed_data' as _i7;
 import 'dart:ui' as _i4;
@@ -21,24 +22,90 @@
 // ignore_for_file: prefer_const_constructors
 // ignore_for_file: unnecessary_parenthesis
 // ignore_for_file: camel_case_types
+// ignore_for_file: subtype_of_sealed_class
 
-class _FakeDownloadListener_0 extends _i1.Fake
-    implements _i2.DownloadListener {}
+class _FakeDownloadListener_0 extends _i1.SmartFake
+    implements _i2.DownloadListener {
+  _FakeDownloadListener_0(
+    Object parent,
+    Invocation parentInvocation,
+  ) : super(
+          parent,
+          parentInvocation,
+        );
+}
 
-class _FakeJavaScriptChannel_1 extends _i1.Fake
-    implements _i2.JavaScriptChannel {}
+class _FakeJavaScriptChannel_1 extends _i1.SmartFake
+    implements _i2.JavaScriptChannel {
+  _FakeJavaScriptChannel_1(
+    Object parent,
+    Invocation parentInvocation,
+  ) : super(
+          parent,
+          parentInvocation,
+        );
+}
 
-class _FakeWebViewPoint_2 extends _i1.Fake implements _i3.WebViewPoint {}
+class _FakeWebViewPoint_2 extends _i1.SmartFake implements _i3.WebViewPoint {
+  _FakeWebViewPoint_2(
+    Object parent,
+    Invocation parentInvocation,
+  ) : super(
+          parent,
+          parentInvocation,
+        );
+}
 
-class _FakeWebChromeClient_3 extends _i1.Fake implements _i2.WebChromeClient {}
+class _FakeWebChromeClient_3 extends _i1.SmartFake
+    implements _i2.WebChromeClient {
+  _FakeWebChromeClient_3(
+    Object parent,
+    Invocation parentInvocation,
+  ) : super(
+          parent,
+          parentInvocation,
+        );
+}
 
-class _FakeWebSettings_4 extends _i1.Fake implements _i2.WebSettings {}
+class _FakeWebSettings_4 extends _i1.SmartFake implements _i2.WebSettings {
+  _FakeWebSettings_4(
+    Object parent,
+    Invocation parentInvocation,
+  ) : super(
+          parent,
+          parentInvocation,
+        );
+}
 
-class _FakeOffset_5 extends _i1.Fake implements _i4.Offset {}
+class _FakeOffset_5 extends _i1.SmartFake implements _i4.Offset {
+  _FakeOffset_5(
+    Object parent,
+    Invocation parentInvocation,
+  ) : super(
+          parent,
+          parentInvocation,
+        );
+}
 
-class _FakeWebView_6 extends _i1.Fake implements _i2.WebView {}
+class _FakeWebView_6 extends _i1.SmartFake implements _i2.WebView {
+  _FakeWebView_6(
+    Object parent,
+    Invocation parentInvocation,
+  ) : super(
+          parent,
+          parentInvocation,
+        );
+}
 
-class _FakeWebViewClient_7 extends _i1.Fake implements _i2.WebViewClient {}
+class _FakeWebViewClient_7 extends _i1.SmartFake implements _i2.WebViewClient {
+  _FakeWebViewClient_7(
+    Object parent,
+    Invocation parentInvocation,
+  ) : super(
+          parent,
+          parentInvocation,
+        );
+}
 
 /// A class which mocks [CookieManagerHostApi].
 ///
@@ -50,14 +117,29 @@
   }
 
   @override
-  _i5.Future<bool> clearCookies() =>
-      (super.noSuchMethod(Invocation.method(#clearCookies, []),
-          returnValue: Future<bool>.value(false)) as _i5.Future<bool>);
+  _i5.Future<bool> clearCookies() => (super.noSuchMethod(
+        Invocation.method(
+          #clearCookies,
+          [],
+        ),
+        returnValue: _i5.Future<bool>.value(false),
+      ) as _i5.Future<bool>);
   @override
-  _i5.Future<void> setCookie(String? arg_url, String? arg_value) =>
-      (super.noSuchMethod(Invocation.method(#setCookie, [arg_url, arg_value]),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+  _i5.Future<void> setCookie(
+    String? arg_url,
+    String? arg_value,
+  ) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #setCookie,
+          [
+            arg_url,
+            arg_value,
+          ],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
 }
 
 /// A class which mocks [DownloadListener].
@@ -69,16 +151,42 @@
   }
 
   @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, []),
-          returnValue: _FakeDownloadListener_0()) as _i2.DownloadListener);
+  _i2.DownloadListener copy() => (super.noSuchMethod(
+        Invocation.method(
+          #copy,
+          [],
+        ),
+        returnValue: _FakeDownloadListener_0(
+          this,
+          Invocation.method(
+            #copy,
+            [],
+          ),
+        ),
+      ) as _i2.DownloadListener);
 }
 
 /// A class which mocks [JavaScriptChannel].
@@ -90,17 +198,29 @@
   }
 
   @override
-  String get channelName =>
-      (super.noSuchMethod(Invocation.getter(#channelName), returnValue: '')
-          as String);
+  String get channelName => (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_1()) as _i2.JavaScriptChannel);
+  _i2.JavaScriptChannel copy() => (super.noSuchMethod(
+        Invocation.method(
+          #copy,
+          [],
+        ),
+        returnValue: _FakeJavaScriptChannel_1(
+          this,
+          Invocation.method(
+            #copy,
+            [],
+          ),
+        ),
+      ) as _i2.JavaScriptChannel);
 }
 
 /// A class which mocks [TestDownloadListenerHostApi].
@@ -113,9 +233,13 @@
   }
 
   @override
-  void create(int? instanceId) =>
-      super.noSuchMethod(Invocation.method(#create, [instanceId]),
-          returnValueForMissingStub: null);
+  void create(int? instanceId) => super.noSuchMethod(
+        Invocation.method(
+          #create,
+          [instanceId],
+        ),
+        returnValueForMissingStub: null,
+      );
 }
 
 /// A class which mocks [TestJavaObjectHostApi].
@@ -128,9 +252,13 @@
   }
 
   @override
-  void dispose(int? identifier) =>
-      super.noSuchMethod(Invocation.method(#dispose, [identifier]),
-          returnValueForMissingStub: null);
+  void dispose(int? identifier) => super.noSuchMethod(
+        Invocation.method(
+          #dispose,
+          [identifier],
+        ),
+        returnValueForMissingStub: null,
+      );
 }
 
 /// A class which mocks [TestJavaScriptChannelHostApi].
@@ -143,9 +271,20 @@
   }
 
   @override
-  void create(int? instanceId, String? channelName) =>
-      super.noSuchMethod(Invocation.method(#create, [instanceId, channelName]),
-          returnValueForMissingStub: null);
+  void create(
+    int? instanceId,
+    String? channelName,
+  ) =>
+      super.noSuchMethod(
+        Invocation.method(
+          #create,
+          [
+            instanceId,
+            channelName,
+          ],
+        ),
+        returnValueForMissingStub: null,
+      );
 }
 
 /// A class which mocks [TestWebChromeClientHostApi].
@@ -158,10 +297,13 @@
   }
 
   @override
-  void create(int? instanceId, int? webViewClientInstanceId) =>
-      super.noSuchMethod(
-          Invocation.method(#create, [instanceId, webViewClientInstanceId]),
-          returnValueForMissingStub: null);
+  void create(int? instanceId) => super.noSuchMethod(
+        Invocation.method(
+          #create,
+          [instanceId],
+        ),
+        returnValueForMissingStub: null,
+      );
 }
 
 /// A class which mocks [TestWebSettingsHostApi].
@@ -174,70 +316,200 @@
   }
 
   @override
-  void create(int? instanceId, int? webViewInstanceId) => super.noSuchMethod(
-      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);
-  @override
-  void setJavaScriptCanOpenWindowsAutomatically(int? instanceId, bool? flag) =>
+  void create(
+    int? instanceId,
+    int? webViewInstanceId,
+  ) =>
       super.noSuchMethod(
-          Invocation.method(
-              #setJavaScriptCanOpenWindowsAutomatically, [instanceId, flag]),
-          returnValueForMissingStub: null);
+        Invocation.method(
+          #create,
+          [
+            instanceId,
+            webViewInstanceId,
+          ],
+        ),
+        returnValueForMissingStub: null,
+      );
   @override
-  void setSupportMultipleWindows(int? instanceId, bool? support) =>
+  void setDomStorageEnabled(
+    int? instanceId,
+    bool? flag,
+  ) =>
       super.noSuchMethod(
-          Invocation.method(#setSupportMultipleWindows, [instanceId, support]),
-          returnValueForMissingStub: null);
+        Invocation.method(
+          #setDomStorageEnabled,
+          [
+            instanceId,
+            flag,
+          ],
+        ),
+        returnValueForMissingStub: null,
+      );
   @override
-  void setJavaScriptEnabled(int? instanceId, bool? flag) => super.noSuchMethod(
-      Invocation.method(#setJavaScriptEnabled, [instanceId, flag]),
-      returnValueForMissingStub: null);
-  @override
-  void setUserAgentString(int? instanceId, String? userAgentString) =>
+  void setJavaScriptCanOpenWindowsAutomatically(
+    int? instanceId,
+    bool? flag,
+  ) =>
       super.noSuchMethod(
-          Invocation.method(#setUserAgentString, [instanceId, userAgentString]),
-          returnValueForMissingStub: null);
+        Invocation.method(
+          #setJavaScriptCanOpenWindowsAutomatically,
+          [
+            instanceId,
+            flag,
+          ],
+        ),
+        returnValueForMissingStub: null,
+      );
   @override
-  void setMediaPlaybackRequiresUserGesture(int? instanceId, bool? require) =>
+  void setSupportMultipleWindows(
+    int? instanceId,
+    bool? support,
+  ) =>
       super.noSuchMethod(
-          Invocation.method(
-              #setMediaPlaybackRequiresUserGesture, [instanceId, require]),
-          returnValueForMissingStub: null);
+        Invocation.method(
+          #setSupportMultipleWindows,
+          [
+            instanceId,
+            support,
+          ],
+        ),
+        returnValueForMissingStub: null,
+      );
   @override
-  void setSupportZoom(int? instanceId, bool? support) => super.noSuchMethod(
-      Invocation.method(#setSupportZoom, [instanceId, support]),
-      returnValueForMissingStub: null);
-  @override
-  void setLoadWithOverviewMode(int? instanceId, bool? overview) =>
+  void setJavaScriptEnabled(
+    int? instanceId,
+    bool? flag,
+  ) =>
       super.noSuchMethod(
-          Invocation.method(#setLoadWithOverviewMode, [instanceId, overview]),
-          returnValueForMissingStub: null);
+        Invocation.method(
+          #setJavaScriptEnabled,
+          [
+            instanceId,
+            flag,
+          ],
+        ),
+        returnValueForMissingStub: null,
+      );
   @override
-  void setUseWideViewPort(int? instanceId, bool? use) => super.noSuchMethod(
-      Invocation.method(#setUseWideViewPort, [instanceId, use]),
-      returnValueForMissingStub: null);
-  @override
-  void setDisplayZoomControls(int? instanceId, bool? enabled) =>
+  void setUserAgentString(
+    int? instanceId,
+    String? userAgentString,
+  ) =>
       super.noSuchMethod(
-          Invocation.method(#setDisplayZoomControls, [instanceId, enabled]),
-          returnValueForMissingStub: null);
+        Invocation.method(
+          #setUserAgentString,
+          [
+            instanceId,
+            userAgentString,
+          ],
+        ),
+        returnValueForMissingStub: null,
+      );
   @override
-  void setBuiltInZoomControls(int? instanceId, bool? enabled) =>
+  void setMediaPlaybackRequiresUserGesture(
+    int? instanceId,
+    bool? require,
+  ) =>
       super.noSuchMethod(
-          Invocation.method(#setBuiltInZoomControls, [instanceId, enabled]),
-          returnValueForMissingStub: null);
+        Invocation.method(
+          #setMediaPlaybackRequiresUserGesture,
+          [
+            instanceId,
+            require,
+          ],
+        ),
+        returnValueForMissingStub: null,
+      );
   @override
-  void setAllowFileAccess(int? instanceId, bool? enabled) => super.noSuchMethod(
-      Invocation.method(#setAllowFileAccess, [instanceId, enabled]),
-      returnValueForMissingStub: null);
+  void setSupportZoom(
+    int? instanceId,
+    bool? support,
+  ) =>
+      super.noSuchMethod(
+        Invocation.method(
+          #setSupportZoom,
+          [
+            instanceId,
+            support,
+          ],
+        ),
+        returnValueForMissingStub: null,
+      );
+  @override
+  void setLoadWithOverviewMode(
+    int? instanceId,
+    bool? overview,
+  ) =>
+      super.noSuchMethod(
+        Invocation.method(
+          #setLoadWithOverviewMode,
+          [
+            instanceId,
+            overview,
+          ],
+        ),
+        returnValueForMissingStub: null,
+      );
+  @override
+  void setUseWideViewPort(
+    int? instanceId,
+    bool? use,
+  ) =>
+      super.noSuchMethod(
+        Invocation.method(
+          #setUseWideViewPort,
+          [
+            instanceId,
+            use,
+          ],
+        ),
+        returnValueForMissingStub: null,
+      );
+  @override
+  void setDisplayZoomControls(
+    int? instanceId,
+    bool? enabled,
+  ) =>
+      super.noSuchMethod(
+        Invocation.method(
+          #setDisplayZoomControls,
+          [
+            instanceId,
+            enabled,
+          ],
+        ),
+        returnValueForMissingStub: null,
+      );
+  @override
+  void setBuiltInZoomControls(
+    int? instanceId,
+    bool? enabled,
+  ) =>
+      super.noSuchMethod(
+        Invocation.method(
+          #setBuiltInZoomControls,
+          [
+            instanceId,
+            enabled,
+          ],
+        ),
+        returnValueForMissingStub: null,
+      );
+  @override
+  void setAllowFileAccess(
+    int? instanceId,
+    bool? enabled,
+  ) =>
+      super.noSuchMethod(
+        Invocation.method(
+          #setAllowFileAccess,
+          [
+            instanceId,
+            enabled,
+          ],
+        ),
+        returnValueForMissingStub: null,
+      );
 }
 
 /// A class which mocks [TestWebStorageHostApi].
@@ -250,13 +522,21 @@
   }
 
   @override
-  void create(int? instanceId) =>
-      super.noSuchMethod(Invocation.method(#create, [instanceId]),
-          returnValueForMissingStub: null);
+  void create(int? instanceId) => super.noSuchMethod(
+        Invocation.method(
+          #create,
+          [instanceId],
+        ),
+        returnValueForMissingStub: null,
+      );
   @override
-  void deleteAllData(int? instanceId) =>
-      super.noSuchMethod(Invocation.method(#deleteAllData, [instanceId]),
-          returnValueForMissingStub: null);
+  void deleteAllData(int? instanceId) => super.noSuchMethod(
+        Invocation.method(
+          #deleteAllData,
+          [instanceId],
+        ),
+        returnValueForMissingStub: null,
+      );
 }
 
 /// A class which mocks [TestWebViewClientHostApi].
@@ -269,10 +549,28 @@
   }
 
   @override
-  void create(int? instanceId, bool? shouldOverrideUrlLoading) =>
+  void create(int? instanceId) => super.noSuchMethod(
+        Invocation.method(
+          #create,
+          [instanceId],
+        ),
+        returnValueForMissingStub: null,
+      );
+  @override
+  void setSynchronousReturnValueForShouldOverrideUrlLoading(
+    int? instanceId,
+    bool? value,
+  ) =>
       super.noSuchMethod(
-          Invocation.method(#create, [instanceId, shouldOverrideUrlLoading]),
-          returnValueForMissingStub: null);
+        Invocation.method(
+          #setSynchronousReturnValueForShouldOverrideUrlLoading,
+          [
+            instanceId,
+            value,
+          ],
+        ),
+        returnValueForMissingStub: null,
+      );
 }
 
 /// A class which mocks [TestWebViewHostApi].
@@ -285,135 +583,338 @@
   }
 
   @override
-  void create(int? instanceId, bool? useHybridComposition) =>
+  void create(
+    int? instanceId,
+    bool? useHybridComposition,
+  ) =>
       super.noSuchMethod(
-          Invocation.method(#create, [instanceId, useHybridComposition]),
-          returnValueForMissingStub: null);
-  @override
-  void dispose(int? instanceId) =>
-      super.noSuchMethod(Invocation.method(#dispose, [instanceId]),
-          returnValueForMissingStub: null);
+        Invocation.method(
+          #create,
+          [
+            instanceId,
+            useHybridComposition,
+          ],
+        ),
+        returnValueForMissingStub: null,
+      );
   @override
   void loadData(
-          int? instanceId, String? data, String? mimeType, String? encoding) =>
+    int? instanceId,
+    String? data,
+    String? mimeType,
+    String? encoding,
+  ) =>
       super.noSuchMethod(
-          Invocation.method(#loadData, [instanceId, data, mimeType, encoding]),
-          returnValueForMissingStub: null);
+        Invocation.method(
+          #loadData,
+          [
+            instanceId,
+            data,
+            mimeType,
+            encoding,
+          ],
+        ),
+        returnValueForMissingStub: null,
+      );
   @override
-  void loadDataWithBaseUrl(int? instanceId, String? baseUrl, String? data,
-          String? mimeType, String? encoding, String? historyUrl) =>
+  void loadDataWithBaseUrl(
+    int? instanceId,
+    String? baseUrl,
+    String? data,
+    String? mimeType,
+    String? encoding,
+    String? historyUrl,
+  ) =>
       super.noSuchMethod(
-          Invocation.method(#loadDataWithBaseUrl,
-              [instanceId, baseUrl, data, mimeType, encoding, historyUrl]),
-          returnValueForMissingStub: null);
+        Invocation.method(
+          #loadDataWithBaseUrl,
+          [
+            instanceId,
+            baseUrl,
+            data,
+            mimeType,
+            encoding,
+            historyUrl,
+          ],
+        ),
+        returnValueForMissingStub: null,
+      );
   @override
-  void loadUrl(int? instanceId, String? url, Map<String?, String?>? headers) =>
+  void loadUrl(
+    int? instanceId,
+    String? url,
+    Map<String?, String?>? headers,
+  ) =>
       super.noSuchMethod(
-          Invocation.method(#loadUrl, [instanceId, url, headers]),
-          returnValueForMissingStub: null);
+        Invocation.method(
+          #loadUrl,
+          [
+            instanceId,
+            url,
+            headers,
+          ],
+        ),
+        returnValueForMissingStub: null,
+      );
   @override
-  void postUrl(int? instanceId, String? url, _i7.Uint8List? data) =>
-      super.noSuchMethod(Invocation.method(#postUrl, [instanceId, url, data]),
-          returnValueForMissingStub: null);
-  @override
-  String? getUrl(int? instanceId) =>
-      (super.noSuchMethod(Invocation.method(#getUrl, [instanceId])) as String?);
-  @override
-  bool canGoBack(int? instanceId) =>
-      (super.noSuchMethod(Invocation.method(#canGoBack, [instanceId]),
-          returnValue: false) as bool);
-  @override
-  bool canGoForward(int? instanceId) =>
-      (super.noSuchMethod(Invocation.method(#canGoForward, [instanceId]),
-          returnValue: false) as bool);
-  @override
-  void goBack(int? instanceId) =>
-      super.noSuchMethod(Invocation.method(#goBack, [instanceId]),
-          returnValueForMissingStub: null);
-  @override
-  void goForward(int? instanceId) =>
-      super.noSuchMethod(Invocation.method(#goForward, [instanceId]),
-          returnValueForMissingStub: null);
-  @override
-  void reload(int? instanceId) =>
-      super.noSuchMethod(Invocation.method(#reload, [instanceId]),
-          returnValueForMissingStub: null);
-  @override
-  void clearCache(int? instanceId, bool? includeDiskFiles) =>
+  void postUrl(
+    int? instanceId,
+    String? url,
+    _i7.Uint8List? data,
+  ) =>
       super.noSuchMethod(
-          Invocation.method(#clearCache, [instanceId, includeDiskFiles]),
-          returnValueForMissingStub: null);
+        Invocation.method(
+          #postUrl,
+          [
+            instanceId,
+            url,
+            data,
+          ],
+        ),
+        returnValueForMissingStub: null,
+      );
+  @override
+  String? getUrl(int? instanceId) => (super.noSuchMethod(Invocation.method(
+        #getUrl,
+        [instanceId],
+      )) as String?);
+  @override
+  bool canGoBack(int? instanceId) => (super.noSuchMethod(
+        Invocation.method(
+          #canGoBack,
+          [instanceId],
+        ),
+        returnValue: false,
+      ) as bool);
+  @override
+  bool canGoForward(int? instanceId) => (super.noSuchMethod(
+        Invocation.method(
+          #canGoForward,
+          [instanceId],
+        ),
+        returnValue: false,
+      ) as bool);
+  @override
+  void goBack(int? instanceId) => super.noSuchMethod(
+        Invocation.method(
+          #goBack,
+          [instanceId],
+        ),
+        returnValueForMissingStub: null,
+      );
+  @override
+  void goForward(int? instanceId) => super.noSuchMethod(
+        Invocation.method(
+          #goForward,
+          [instanceId],
+        ),
+        returnValueForMissingStub: null,
+      );
+  @override
+  void reload(int? instanceId) => super.noSuchMethod(
+        Invocation.method(
+          #reload,
+          [instanceId],
+        ),
+        returnValueForMissingStub: null,
+      );
+  @override
+  void clearCache(
+    int? instanceId,
+    bool? includeDiskFiles,
+  ) =>
+      super.noSuchMethod(
+        Invocation.method(
+          #clearCache,
+          [
+            instanceId,
+            includeDiskFiles,
+          ],
+        ),
+        returnValueForMissingStub: null,
+      );
   @override
   _i5.Future<String?> evaluateJavascript(
-          int? instanceId, String? javascriptString) =>
+    int? instanceId,
+    String? javascriptString,
+  ) =>
       (super.noSuchMethod(
+        Invocation.method(
+          #evaluateJavascript,
+          [
+            instanceId,
+            javascriptString,
+          ],
+        ),
+        returnValue: _i5.Future<String?>.value(),
+      ) as _i5.Future<String?>);
+  @override
+  String? getTitle(int? instanceId) => (super.noSuchMethod(Invocation.method(
+        #getTitle,
+        [instanceId],
+      )) as String?);
+  @override
+  void scrollTo(
+    int? instanceId,
+    int? x,
+    int? y,
+  ) =>
+      super.noSuchMethod(
+        Invocation.method(
+          #scrollTo,
+          [
+            instanceId,
+            x,
+            y,
+          ],
+        ),
+        returnValueForMissingStub: null,
+      );
+  @override
+  void scrollBy(
+    int? instanceId,
+    int? x,
+    int? y,
+  ) =>
+      super.noSuchMethod(
+        Invocation.method(
+          #scrollBy,
+          [
+            instanceId,
+            x,
+            y,
+          ],
+        ),
+        returnValueForMissingStub: null,
+      );
+  @override
+  int getScrollX(int? instanceId) => (super.noSuchMethod(
+        Invocation.method(
+          #getScrollX,
+          [instanceId],
+        ),
+        returnValue: 0,
+      ) as int);
+  @override
+  int getScrollY(int? instanceId) => (super.noSuchMethod(
+        Invocation.method(
+          #getScrollY,
+          [instanceId],
+        ),
+        returnValue: 0,
+      ) as int);
+  @override
+  _i3.WebViewPoint getScrollPosition(int? instanceId) => (super.noSuchMethod(
+        Invocation.method(
+          #getScrollPosition,
+          [instanceId],
+        ),
+        returnValue: _FakeWebViewPoint_2(
+          this,
           Invocation.method(
-              #evaluateJavascript, [instanceId, javascriptString]),
-          returnValue: Future<String?>.value()) as _i5.Future<String?>);
-  @override
-  String? getTitle(int? instanceId) =>
-      (super.noSuchMethod(Invocation.method(#getTitle, [instanceId]))
-          as String?);
-  @override
-  void scrollTo(int? instanceId, int? x, int? y) =>
-      super.noSuchMethod(Invocation.method(#scrollTo, [instanceId, x, y]),
-          returnValueForMissingStub: null);
-  @override
-  void scrollBy(int? instanceId, int? x, int? y) =>
-      super.noSuchMethod(Invocation.method(#scrollBy, [instanceId, x, y]),
-          returnValueForMissingStub: null);
-  @override
-  int getScrollX(int? instanceId) =>
-      (super.noSuchMethod(Invocation.method(#getScrollX, [instanceId]),
-          returnValue: 0) as int);
-  @override
-  int getScrollY(int? instanceId) =>
-      (super.noSuchMethod(Invocation.method(#getScrollY, [instanceId]),
-          returnValue: 0) as int);
-  @override
-  _i3.WebViewPoint getScrollPosition(int? instanceId) =>
-      (super.noSuchMethod(Invocation.method(#getScrollPosition, [instanceId]),
-          returnValue: _FakeWebViewPoint_2()) as _i3.WebViewPoint);
+            #getScrollPosition,
+            [instanceId],
+          ),
+        ),
+      ) as _i3.WebViewPoint);
   @override
   void setWebContentsDebuggingEnabled(bool? enabled) => super.noSuchMethod(
-      Invocation.method(#setWebContentsDebuggingEnabled, [enabled]),
-      returnValueForMissingStub: null);
+        Invocation.method(
+          #setWebContentsDebuggingEnabled,
+          [enabled],
+        ),
+        returnValueForMissingStub: null,
+      );
   @override
-  void setWebViewClient(int? instanceId, int? webViewClientInstanceId) =>
+  void setWebViewClient(
+    int? instanceId,
+    int? webViewClientInstanceId,
+  ) =>
       super.noSuchMethod(
-          Invocation.method(
-              #setWebViewClient, [instanceId, webViewClientInstanceId]),
-          returnValueForMissingStub: null);
+        Invocation.method(
+          #setWebViewClient,
+          [
+            instanceId,
+            webViewClientInstanceId,
+          ],
+        ),
+        returnValueForMissingStub: null,
+      );
   @override
   void addJavaScriptChannel(
-          int? instanceId, int? javaScriptChannelInstanceId) =>
+    int? instanceId,
+    int? javaScriptChannelInstanceId,
+  ) =>
       super.noSuchMethod(
-          Invocation.method(
-              #addJavaScriptChannel, [instanceId, javaScriptChannelInstanceId]),
-          returnValueForMissingStub: null);
+        Invocation.method(
+          #addJavaScriptChannel,
+          [
+            instanceId,
+            javaScriptChannelInstanceId,
+          ],
+        ),
+        returnValueForMissingStub: null,
+      );
   @override
   void removeJavaScriptChannel(
-          int? instanceId, int? javaScriptChannelInstanceId) =>
+    int? instanceId,
+    int? javaScriptChannelInstanceId,
+  ) =>
       super.noSuchMethod(
-          Invocation.method(#removeJavaScriptChannel,
-              [instanceId, javaScriptChannelInstanceId]),
-          returnValueForMissingStub: null);
+        Invocation.method(
+          #removeJavaScriptChannel,
+          [
+            instanceId,
+            javaScriptChannelInstanceId,
+          ],
+        ),
+        returnValueForMissingStub: null,
+      );
   @override
-  void setDownloadListener(int? instanceId, int? listenerInstanceId) =>
+  void setDownloadListener(
+    int? instanceId,
+    int? listenerInstanceId,
+  ) =>
       super.noSuchMethod(
-          Invocation.method(
-              #setDownloadListener, [instanceId, listenerInstanceId]),
-          returnValueForMissingStub: null);
+        Invocation.method(
+          #setDownloadListener,
+          [
+            instanceId,
+            listenerInstanceId,
+          ],
+        ),
+        returnValueForMissingStub: null,
+      );
   @override
-  void setWebChromeClient(int? instanceId, int? clientInstanceId) =>
+  void setWebChromeClient(
+    int? instanceId,
+    int? clientInstanceId,
+  ) =>
       super.noSuchMethod(
-          Invocation.method(
-              #setWebChromeClient, [instanceId, clientInstanceId]),
-          returnValueForMissingStub: null);
+        Invocation.method(
+          #setWebChromeClient,
+          [
+            instanceId,
+            clientInstanceId,
+          ],
+        ),
+        returnValueForMissingStub: null,
+      );
   @override
-  void setBackgroundColor(int? instanceId, int? color) => super.noSuchMethod(
-      Invocation.method(#setBackgroundColor, [instanceId, color]),
-      returnValueForMissingStub: null);
+  void setBackgroundColor(
+    int? instanceId,
+    int? color,
+  ) =>
+      super.noSuchMethod(
+        Invocation.method(
+          #setBackgroundColor,
+          [
+            instanceId,
+            color,
+          ],
+        ),
+        returnValueForMissingStub: null,
+      );
 }
 
 /// A class which mocks [TestAssetManagerHostApi].
@@ -426,13 +927,21 @@
   }
 
   @override
-  List<String?> list(String? path) =>
-      (super.noSuchMethod(Invocation.method(#list, [path]),
-          returnValue: <String?>[]) as List<String?>);
+  List<String?> list(String? path) => (super.noSuchMethod(
+        Invocation.method(
+          #list,
+          [path],
+        ),
+        returnValue: <String?>[],
+      ) as List<String?>);
   @override
-  String getAssetFilePathByName(String? name) =>
-      (super.noSuchMethod(Invocation.method(#getAssetFilePathByName, [name]),
-          returnValue: '') as String);
+  String getAssetFilePathByName(String? name) => (super.noSuchMethod(
+        Invocation.method(
+          #getAssetFilePathByName,
+          [name],
+        ),
+        returnValue: '',
+      ) as String);
 }
 
 /// A class which mocks [WebChromeClient].
@@ -444,13 +953,19 @@
   }
 
   @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);
+  _i2.WebChromeClient copy() => (super.noSuchMethod(
+        Invocation.method(
+          #copy,
+          [],
+        ),
+        returnValue: _FakeWebChromeClient_3(
+          this,
+          Invocation.method(
+            #copy,
+            [],
+          ),
+        ),
+      ) as _i2.WebChromeClient);
 }
 
 /// A class which mocks [WebView].
@@ -462,153 +977,306 @@
   }
 
   @override
-  bool get useHybridComposition =>
-      (super.noSuchMethod(Invocation.getter(#useHybridComposition),
-          returnValue: false) as bool);
+  bool get useHybridComposition => (super.noSuchMethod(
+        Invocation.getter(#useHybridComposition),
+        returnValue: false,
+      ) as bool);
   @override
-  _i2.WebSettings get settings =>
-      (super.noSuchMethod(Invocation.getter(#settings),
-          returnValue: _FakeWebSettings_4()) as _i2.WebSettings);
+  _i2.WebSettings get settings => (super.noSuchMethod(
+        Invocation.getter(#settings),
+        returnValue: _FakeWebSettings_4(
+          this,
+          Invocation.getter(#settings),
+        ),
+      ) as _i2.WebSettings);
   @override
-  _i5.Future<void> loadData(
-          {String? data, String? mimeType, String? encoding}) =>
+  _i5.Future<void> loadData({
+    required String? data,
+    String? mimeType,
+    String? encoding,
+  }) =>
       (super.noSuchMethod(
-          Invocation.method(#loadData, [],
-              {#data: data, #mimeType: mimeType, #encoding: encoding}),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+        Invocation.method(
+          #loadData,
+          [],
+          {
+            #data: data,
+            #mimeType: mimeType,
+            #encoding: encoding,
+          },
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
   @override
-  _i5.Future<void> loadDataWithBaseUrl(
-          {String? baseUrl,
-          String? data,
-          String? mimeType,
-          String? encoding,
-          String? historyUrl}) =>
+  _i5.Future<void> loadDataWithBaseUrl({
+    String? baseUrl,
+    required String? data,
+    String? mimeType,
+    String? encoding,
+    String? historyUrl,
+  }) =>
       (super.noSuchMethod(
-          Invocation.method(#loadDataWithBaseUrl, [], {
+        Invocation.method(
+          #loadDataWithBaseUrl,
+          [],
+          {
             #baseUrl: baseUrl,
             #data: data,
             #mimeType: mimeType,
             #encoding: encoding,
-            #historyUrl: historyUrl
-          }),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+            #historyUrl: historyUrl,
+          },
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
   @override
-  _i5.Future<void> loadUrl(String? url, Map<String, String>? headers) =>
-      (super.noSuchMethod(Invocation.method(#loadUrl, [url, headers]),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+  _i5.Future<void> loadUrl(
+    String? url,
+    Map<String, String>? headers,
+  ) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #loadUrl,
+          [
+            url,
+            headers,
+          ],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
   @override
-  _i5.Future<void> postUrl(String? url, _i7.Uint8List? data) =>
-      (super.noSuchMethod(Invocation.method(#postUrl, [url, data]),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+  _i5.Future<void> postUrl(
+    String? url,
+    _i7.Uint8List? data,
+  ) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #postUrl,
+          [
+            url,
+            data,
+          ],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
   @override
-  _i5.Future<String?> getUrl() =>
-      (super.noSuchMethod(Invocation.method(#getUrl, []),
-          returnValue: Future<String?>.value()) as _i5.Future<String?>);
+  _i5.Future<String?> getUrl() => (super.noSuchMethod(
+        Invocation.method(
+          #getUrl,
+          [],
+        ),
+        returnValue: _i5.Future<String?>.value(),
+      ) as _i5.Future<String?>);
   @override
-  _i5.Future<bool> canGoBack() =>
-      (super.noSuchMethod(Invocation.method(#canGoBack, []),
-          returnValue: Future<bool>.value(false)) as _i5.Future<bool>);
+  _i5.Future<bool> canGoBack() => (super.noSuchMethod(
+        Invocation.method(
+          #canGoBack,
+          [],
+        ),
+        returnValue: _i5.Future<bool>.value(false),
+      ) as _i5.Future<bool>);
   @override
-  _i5.Future<bool> canGoForward() =>
-      (super.noSuchMethod(Invocation.method(#canGoForward, []),
-          returnValue: Future<bool>.value(false)) as _i5.Future<bool>);
+  _i5.Future<bool> canGoForward() => (super.noSuchMethod(
+        Invocation.method(
+          #canGoForward,
+          [],
+        ),
+        returnValue: _i5.Future<bool>.value(false),
+      ) as _i5.Future<bool>);
   @override
-  _i5.Future<void> goBack() =>
-      (super.noSuchMethod(Invocation.method(#goBack, []),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+  _i5.Future<void> goBack() => (super.noSuchMethod(
+        Invocation.method(
+          #goBack,
+          [],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
   @override
-  _i5.Future<void> goForward() =>
-      (super.noSuchMethod(Invocation.method(#goForward, []),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+  _i5.Future<void> goForward() => (super.noSuchMethod(
+        Invocation.method(
+          #goForward,
+          [],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
   @override
-  _i5.Future<void> reload() =>
-      (super.noSuchMethod(Invocation.method(#reload, []),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+  _i5.Future<void> reload() => (super.noSuchMethod(
+        Invocation.method(
+          #reload,
+          [],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
   @override
-  _i5.Future<void> clearCache(bool? includeDiskFiles) =>
-      (super.noSuchMethod(Invocation.method(#clearCache, [includeDiskFiles]),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+  _i5.Future<void> clearCache(bool? includeDiskFiles) => (super.noSuchMethod(
+        Invocation.method(
+          #clearCache,
+          [includeDiskFiles],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
   @override
-  _i5.Future<String?> evaluateJavascript(String? javascriptString) => (super
-      .noSuchMethod(Invocation.method(#evaluateJavascript, [javascriptString]),
-          returnValue: Future<String?>.value()) as _i5.Future<String?>);
+  _i5.Future<String?> evaluateJavascript(String? javascriptString) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #evaluateJavascript,
+          [javascriptString],
+        ),
+        returnValue: _i5.Future<String?>.value(),
+      ) as _i5.Future<String?>);
   @override
-  _i5.Future<String?> getTitle() =>
-      (super.noSuchMethod(Invocation.method(#getTitle, []),
-          returnValue: Future<String?>.value()) as _i5.Future<String?>);
+  _i5.Future<String?> getTitle() => (super.noSuchMethod(
+        Invocation.method(
+          #getTitle,
+          [],
+        ),
+        returnValue: _i5.Future<String?>.value(),
+      ) as _i5.Future<String?>);
   @override
-  _i5.Future<void> scrollTo(int? x, int? y) =>
-      (super.noSuchMethod(Invocation.method(#scrollTo, [x, y]),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+  _i5.Future<void> scrollTo(
+    int? x,
+    int? y,
+  ) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #scrollTo,
+          [
+            x,
+            y,
+          ],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
   @override
-  _i5.Future<void> scrollBy(int? x, int? y) =>
-      (super.noSuchMethod(Invocation.method(#scrollBy, [x, y]),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+  _i5.Future<void> scrollBy(
+    int? x,
+    int? y,
+  ) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #scrollBy,
+          [
+            x,
+            y,
+          ],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
   @override
-  _i5.Future<int> getScrollX() =>
-      (super.noSuchMethod(Invocation.method(#getScrollX, []),
-          returnValue: Future<int>.value(0)) as _i5.Future<int>);
+  _i5.Future<int> getScrollX() => (super.noSuchMethod(
+        Invocation.method(
+          #getScrollX,
+          [],
+        ),
+        returnValue: _i5.Future<int>.value(0),
+      ) as _i5.Future<int>);
   @override
-  _i5.Future<int> getScrollY() =>
-      (super.noSuchMethod(Invocation.method(#getScrollY, []),
-          returnValue: Future<int>.value(0)) as _i5.Future<int>);
+  _i5.Future<int> getScrollY() => (super.noSuchMethod(
+        Invocation.method(
+          #getScrollY,
+          [],
+        ),
+        returnValue: _i5.Future<int>.value(0),
+      ) as _i5.Future<int>);
   @override
-  _i5.Future<_i4.Offset> getScrollPosition() =>
-      (super.noSuchMethod(Invocation.method(#getScrollPosition, []),
-              returnValue: Future<_i4.Offset>.value(_FakeOffset_5()))
-          as _i5.Future<_i4.Offset>);
+  _i5.Future<_i4.Offset> getScrollPosition() => (super.noSuchMethod(
+        Invocation.method(
+          #getScrollPosition,
+          [],
+        ),
+        returnValue: _i5.Future<_i4.Offset>.value(_FakeOffset_5(
+          this,
+          Invocation.method(
+            #getScrollPosition,
+            [],
+          ),
+        )),
+      ) as _i5.Future<_i4.Offset>);
   @override
   _i5.Future<void> setWebViewClient(_i2.WebViewClient? webViewClient) =>
-      (super.noSuchMethod(Invocation.method(#setWebViewClient, [webViewClient]),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+      (super.noSuchMethod(
+        Invocation.method(
+          #setWebViewClient,
+          [webViewClient],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
   @override
   _i5.Future<void> addJavaScriptChannel(
           _i2.JavaScriptChannel? javaScriptChannel) =>
       (super.noSuchMethod(
-          Invocation.method(#addJavaScriptChannel, [javaScriptChannel]),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+        Invocation.method(
+          #addJavaScriptChannel,
+          [javaScriptChannel],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
   @override
   _i5.Future<void> removeJavaScriptChannel(
           _i2.JavaScriptChannel? javaScriptChannel) =>
       (super.noSuchMethod(
-          Invocation.method(#removeJavaScriptChannel, [javaScriptChannel]),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+        Invocation.method(
+          #removeJavaScriptChannel,
+          [javaScriptChannel],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
   @override
   _i5.Future<void> setDownloadListener(_i2.DownloadListener? listener) =>
-      (super.noSuchMethod(Invocation.method(#setDownloadListener, [listener]),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+      (super.noSuchMethod(
+        Invocation.method(
+          #setDownloadListener,
+          [listener],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
   @override
   _i5.Future<void> setWebChromeClient(_i2.WebChromeClient? client) =>
-      (super.noSuchMethod(Invocation.method(#setWebChromeClient, [client]),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+      (super.noSuchMethod(
+        Invocation.method(
+          #setWebChromeClient,
+          [client],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
   @override
-  _i5.Future<void> setBackgroundColor(_i4.Color? color) =>
-      (super.noSuchMethod(Invocation.method(#setBackgroundColor, [color]),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
+  _i5.Future<void> setBackgroundColor(_i4.Color? color) => (super.noSuchMethod(
+        Invocation.method(
+          #setBackgroundColor,
+          [color],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.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);
+  _i2.WebView copy() => (super.noSuchMethod(
+        Invocation.method(
+          #copy,
+          [],
+        ),
+        returnValue: _FakeWebView_6(
+          this,
+          Invocation.method(
+            #copy,
+            [],
+          ),
+        ),
+      ) as _i2.WebView);
 }
 
 /// A class which mocks [WebViewClient].
@@ -620,39 +1288,28 @@
   }
 
   @override
-  bool get shouldOverrideUrlLoading =>
-      (super.noSuchMethod(Invocation.getter(#shouldOverrideUrlLoading),
-          returnValue: false) as bool);
+  _i5.Future<void> setSynchronousReturnValueForShouldOverrideUrlLoading(
+          bool? value) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #setSynchronousReturnValueForShouldOverrideUrlLoading,
+          [value],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
   @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(
+  _i2.WebViewClient copy() => (super.noSuchMethod(
+        Invocation.method(
+          #copy,
+          [],
+        ),
+        returnValue: _FakeWebViewClient_7(
+          this,
           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);
+            #copy,
+            [],
+          ),
+        ),
+      ) as _i2.WebViewClient);
 }
diff --git a/packages/webview_flutter/webview_flutter_android/test/surface_android_test.dart b/packages/webview_flutter/webview_flutter_android/test/legacy/surface_android_test.dart
similarity index 94%
rename from packages/webview_flutter/webview_flutter_android/test/surface_android_test.dart
rename to packages/webview_flutter/webview_flutter_android/test/legacy/surface_android_test.dart
index 63e752b..1657e1b 100644
--- a/packages/webview_flutter/webview_flutter_android/test/surface_android_test.dart
+++ b/packages/webview_flutter/webview_flutter_android/test/legacy/surface_android_test.dart
@@ -7,8 +7,8 @@
 import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
 import 'package:flutter_test/flutter_test.dart';
-import 'package:webview_flutter_android/webview_surface_android.dart';
-import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
+import 'package:webview_flutter_android/src/legacy/webview_surface_android.dart';
+import 'package:webview_flutter_platform_interface/src/webview_flutter_platform_interface_legacy.dart';
 
 void main() {
   TestWidgetsFlutterBinding.ensureInitialized();
diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_cookie_manager_test.dart b/packages/webview_flutter/webview_flutter_android/test/legacy/webview_android_cookie_manager_test.dart
similarity index 89%
rename from packages/webview_flutter/webview_flutter_android/test/webview_android_cookie_manager_test.dart
rename to packages/webview_flutter/webview_flutter_android/test/legacy/webview_android_cookie_manager_test.dart
index 4f274ff..e4cd616 100644
--- a/packages/webview_flutter/webview_flutter_android/test/webview_android_cookie_manager_test.dart
+++ b/packages/webview_flutter/webview_flutter_android/test/legacy/webview_android_cookie_manager_test.dart
@@ -7,8 +7,8 @@
 import 'package:mockito/mockito.dart';
 import 'package:webview_flutter_android/src/android_webview.dart'
     as android_webview;
-import 'package:webview_flutter_android/webview_android_cookie_manager.dart';
-import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
+import 'package:webview_flutter_android/src/legacy/webview_android_cookie_manager.dart';
+import 'package:webview_flutter_platform_interface/src/webview_flutter_platform_interface_legacy.dart';
 
 import 'webview_android_cookie_manager_test.mocks.dart';
 
diff --git a/packages/webview_flutter/webview_flutter_android/test/legacy/webview_android_cookie_manager_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/legacy/webview_android_cookie_manager_test.mocks.dart
new file mode 100644
index 0000000..85aed14
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_android/test/legacy/webview_android_cookie_manager_test.mocks.dart
@@ -0,0 +1,54 @@
+// Mocks generated by Mockito 5.3.2 from annotations
+// in webview_flutter_android/test/legacy/webview_android_cookie_manager_test.dart.
+// Do not manually edit this file.
+
+// ignore_for_file: no_leading_underscores_for_library_prefixes
+import 'dart:async' as _i3;
+
+import 'package:mockito/mockito.dart' as _i1;
+import 'package:webview_flutter_android/src/android_webview.dart' as _i2;
+
+// ignore_for_file: type=lint
+// ignore_for_file: avoid_redundant_argument_values
+// ignore_for_file: avoid_setters_without_getters
+// ignore_for_file: comment_references
+// ignore_for_file: implementation_imports
+// ignore_for_file: invalid_use_of_visible_for_testing_member
+// ignore_for_file: prefer_const_constructors
+// ignore_for_file: unnecessary_parenthesis
+// ignore_for_file: camel_case_types
+// ignore_for_file: subtype_of_sealed_class
+
+/// A class which mocks [CookieManager].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockCookieManager extends _i1.Mock implements _i2.CookieManager {
+  MockCookieManager() {
+    _i1.throwOnMissingStub(this);
+  }
+
+  @override
+  _i3.Future<void> setCookie(
+    String? url,
+    String? value,
+  ) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #setCookie,
+          [
+            url,
+            value,
+          ],
+        ),
+        returnValue: _i3.Future<void>.value(),
+        returnValueForMissingStub: _i3.Future<void>.value(),
+      ) as _i3.Future<void>);
+  @override
+  _i3.Future<bool> clearCookies() => (super.noSuchMethod(
+        Invocation.method(
+          #clearCookies,
+          [],
+        ),
+        returnValue: _i3.Future<bool>.value(false),
+      ) as _i3.Future<bool>);
+}
diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart b/packages/webview_flutter/webview_flutter_android/test/legacy/webview_android_widget_test.dart
similarity index 75%
rename from packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart
rename to packages/webview_flutter/webview_flutter_android/test/legacy/webview_android_widget_test.dart
index b6d6239..909607a 100644
--- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart
+++ b/packages/webview_flutter/webview_flutter_android/test/legacy/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';
@@ -13,11 +12,11 @@
     as android_webview;
 import 'package:webview_flutter_android/src/android_webview_api_impls.dart';
 import 'package:webview_flutter_android/src/instance_manager.dart';
-import 'package:webview_flutter_android/webview_android_widget.dart';
-import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
+import 'package:webview_flutter_android/src/legacy/webview_android_widget.dart';
+import 'package:webview_flutter_platform_interface/src/webview_flutter_platform_interface_legacy.dart';
 
-import 'android_webview_test.mocks.dart' show MockTestWebViewHostApi;
-import 'test_android_webview.pigeon.dart';
+import '../android_webview_test.mocks.dart' show MockTestWebViewHostApi;
+import '../test_android_webview.pigeon.dart';
 import 'webview_android_widget_test.mocks.dart';
 
 @GenerateMocks(<Type>[
@@ -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 MockWebViewClient mockWebViewClient;
+    late android_webview.DownloadListener downloadListener;
+    late android_webview.WebChromeClient webChromeClient;
 
     late MockJavascriptChannelRegistry mockJavascriptChannelRegistry;
 
@@ -58,12 +57,21 @@
       mockWebView = MockWebView();
       mockWebSettings = MockWebSettings();
       mockWebStorage = MockWebStorage();
+      mockWebViewClient = MockWebViewClient();
       when(mockWebView.settings).thenReturn(mockWebSettings);
 
       mockWebViewProxy = MockWebViewProxy();
       when(mockWebViewProxy.createWebView(
         useHybridComposition: anyNamed('useHybridComposition'),
       )).thenReturn(mockWebView);
+      when(mockWebViewProxy.createWebViewClient(
+        onPageStarted: anyNamed('onPageStarted'),
+        onPageFinished: anyNamed('onPageFinished'),
+        onReceivedError: anyNamed('onReceivedError'),
+        onReceivedRequestError: anyNamed('onReceivedRequestError'),
+        requestLoading: anyNamed('requestLoading'),
+        urlLoading: anyNamed('urlLoading'),
+      )).thenReturn(mockWebViewClient);
 
       mockCallbacksHandler = MockWebViewPlatformCallbacksHandler();
       mockJavascriptChannelRegistry = MockJavascriptChannelRegistry();
@@ -97,7 +105,7 @@
         },
       ));
 
-      webViewClient = testController.webViewClient;
+      mockWebViewClient = testController.webViewClient as MockWebViewClient;
       downloadListener = testController.downloadListener;
       webChromeClient = testController.webChromeClient;
     }
@@ -114,9 +122,9 @@
       verify(mockWebSettings.setBuiltInZoomControls(true));
 
       verifyInOrder(<Future<void>>[
-        mockWebView.setWebViewClient(webViewClient),
         mockWebView.setDownloadListener(downloadListener),
         mockWebView.setWebChromeClient(webChromeClient),
+        mockWebView.setWebViewClient(mockWebViewClient),
       ]);
     });
 
@@ -225,6 +233,16 @@
         });
 
         testWidgets('hasNavigationDelegate', (WidgetTester tester) async {
+          final MockWebViewClient mockWebViewClient = MockWebViewClient();
+          when(mockWebViewProxy.createWebViewClient(
+            onPageStarted: anyNamed('onPageStarted'),
+            onPageFinished: anyNamed('onPageFinished'),
+            onReceivedError: anyNamed('onReceivedError'),
+            onReceivedRequestError: anyNamed('onReceivedRequestError'),
+            requestLoading: anyNamed('requestLoading'),
+            urlLoading: anyNamed('urlLoading'),
+          )).thenReturn(mockWebViewClient);
+
           await buildWidget(
             tester,
             creationParams: CreationParams(
@@ -235,8 +253,10 @@
             ),
           );
 
-          expect(testController.webViewClient.handlesNavigation, isTrue);
-          expect(testController.webViewClient.shouldOverrideUrlLoading, isTrue);
+          verify(
+            mockWebViewClient
+                .setSynchronousReturnValueForShouldOverrideUrlLoading(true),
+          );
         });
 
         testWidgets('debuggingEnabled true', (WidgetTester tester) async {
@@ -693,20 +713,53 @@
     group('WebViewPlatformCallbacksHandler', () {
       testWidgets('onPageStarted', (WidgetTester tester) async {
         await buildWidget(tester);
-        webViewClient.onPageStarted(mockWebView, 'https://google.com');
+        final void Function(android_webview.WebView, String) onPageStarted =
+            verify(mockWebViewProxy.createWebViewClient(
+          onPageStarted: captureAnyNamed('onPageStarted'),
+          onPageFinished: anyNamed('onPageFinished'),
+          onReceivedError: anyNamed('onReceivedError'),
+          onReceivedRequestError: anyNamed('onReceivedRequestError'),
+          requestLoading: anyNamed('requestLoading'),
+          urlLoading: anyNamed('urlLoading'),
+        )).captured.single as Function(android_webview.WebView, String);
+
+        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');
+
+        final void Function(android_webview.WebView, String) onPageFinished =
+            verify(mockWebViewProxy.createWebViewClient(
+          onPageStarted: anyNamed('onPageStarted'),
+          onPageFinished: captureAnyNamed('onPageFinished'),
+          onReceivedError: anyNamed('onReceivedError'),
+          onReceivedRequestError: anyNamed('onReceivedRequestError'),
+          requestLoading: anyNamed('requestLoading'),
+          urlLoading: anyNamed('urlLoading'),
+        )).captured.single as Function(android_webview.WebView, String);
+
+        onPageFinished(mockWebView, 'https://google.com');
         verify(mockCallbacksHandler.onPageFinished('https://google.com'));
       });
 
       testWidgets('onWebResourceError from onReceivedError',
           (WidgetTester tester) async {
         await buildWidget(tester);
-        webViewClient.onReceivedError(
+
+        final void Function(android_webview.WebView, int, String, String)
+            onReceivedError = verify(mockWebViewProxy.createWebViewClient(
+          onPageStarted: anyNamed('onPageStarted'),
+          onPageFinished: anyNamed('onPageFinished'),
+          onReceivedError: captureAnyNamed('onReceivedError'),
+          onReceivedRequestError: anyNamed('onReceivedRequestError'),
+          requestLoading: anyNamed('requestLoading'),
+          urlLoading: anyNamed('urlLoading'),
+        )).captured.single as Function(
+                android_webview.WebView, int, String, String);
+
+        onReceivedError(
           mockWebView,
           android_webview.WebViewClient.errorAuthentication,
           'description',
@@ -727,7 +780,25 @@
       testWidgets('onWebResourceError from onReceivedRequestError',
           (WidgetTester tester) async {
         await buildWidget(tester);
-        webViewClient.onReceivedRequestError(
+
+        final void Function(
+          android_webview.WebView,
+          android_webview.WebResourceRequest,
+          android_webview.WebResourceError,
+        ) onReceivedRequestError = verify(mockWebViewProxy.createWebViewClient(
+          onPageStarted: anyNamed('onPageStarted'),
+          onPageFinished: anyNamed('onPageFinished'),
+          onReceivedError: anyNamed('onReceivedError'),
+          onReceivedRequestError: captureAnyNamed('onReceivedRequestError'),
+          requestLoading: anyNamed('requestLoading'),
+          urlLoading: anyNamed('urlLoading'),
+        )).captured.single as Function(
+          android_webview.WebView,
+          android_webview.WebResourceRequest,
+          android_webview.WebResourceError,
+        );
+
+        onReceivedRequestError(
           mockWebView,
           android_webview.WebResourceRequest(
             url: 'https://google.com',
@@ -762,7 +833,17 @@
           url: 'https://google.com',
         )).thenReturn(true);
 
-        webViewClient.urlLoading(mockWebView, 'https://google.com');
+        final void Function(android_webview.WebView, String) urlLoading =
+            verify(mockWebViewProxy.createWebViewClient(
+          onPageStarted: anyNamed('onPageStarted'),
+          onPageFinished: anyNamed('onPageFinished'),
+          onReceivedError: anyNamed('onReceivedError'),
+          onReceivedRequestError: anyNamed('onReceivedRequestError'),
+          requestLoading: anyNamed('requestLoading'),
+          urlLoading: captureAnyNamed('urlLoading'),
+        )).captured.single as Function(android_webview.WebView, String);
+
+        urlLoading(mockWebView, 'https://google.com');
         verify(mockCallbacksHandler.onNavigationRequest(
           url: 'https://google.com',
           isForMainFrame: true,
@@ -778,7 +859,22 @@
           url: 'https://google.com',
         )).thenReturn(true);
 
-        webViewClient.requestLoading(
+        final void Function(
+          android_webview.WebView,
+          android_webview.WebResourceRequest,
+        ) requestLoading = verify(mockWebViewProxy.createWebViewClient(
+          onPageStarted: anyNamed('onPageStarted'),
+          onPageFinished: anyNamed('onPageFinished'),
+          onReceivedError: anyNamed('onReceivedError'),
+          onReceivedRequestError: anyNamed('onReceivedRequestError'),
+          requestLoading: captureAnyNamed('requestLoading'),
+          urlLoading: anyNamed('urlLoading'),
+        )).captured.single as Function(
+          android_webview.WebView,
+          android_webview.WebResourceRequest,
+        );
+
+        requestLoading(
           mockWebView,
           android_webview.WebResourceRequest(
             url: 'https://google.com',
@@ -843,192 +939,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/legacy/webview_android_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/legacy/webview_android_widget_test.mocks.dart
new file mode 100644
index 0000000..1f16c29
--- /dev/null
+++ b/packages/webview_flutter/webview_flutter_android/test/legacy/webview_android_widget_test.mocks.dart
@@ -0,0 +1,1016 @@
+// Mocks generated by Mockito 5.3.2 from annotations
+// in webview_flutter_android/test/legacy/webview_android_widget_test.dart.
+// Do not manually edit this file.
+
+// ignore_for_file: no_leading_underscores_for_library_prefixes
+import 'dart:async' as _i5;
+import 'dart:typed_data' as _i6;
+import 'dart:ui' as _i3;
+
+import 'package:mockito/mockito.dart' as _i1;
+import 'package:webview_flutter_android/src/android_webview.dart' as _i2;
+import 'package:webview_flutter_android/src/legacy/webview_android_widget.dart'
+    as _i7;
+import 'package:webview_flutter_platform_interface/src/webview_flutter_platform_interface_legacy.dart'
+    as _i4;
+
+// ignore_for_file: type=lint
+// ignore_for_file: avoid_redundant_argument_values
+// ignore_for_file: avoid_setters_without_getters
+// ignore_for_file: comment_references
+// ignore_for_file: implementation_imports
+// ignore_for_file: invalid_use_of_visible_for_testing_member
+// ignore_for_file: prefer_const_constructors
+// ignore_for_file: unnecessary_parenthesis
+// ignore_for_file: camel_case_types
+// ignore_for_file: subtype_of_sealed_class
+
+class _FakeWebSettings_0 extends _i1.SmartFake implements _i2.WebSettings {
+  _FakeWebSettings_0(
+    Object parent,
+    Invocation parentInvocation,
+  ) : super(
+          parent,
+          parentInvocation,
+        );
+}
+
+class _FakeWebStorage_1 extends _i1.SmartFake implements _i2.WebStorage {
+  _FakeWebStorage_1(
+    Object parent,
+    Invocation parentInvocation,
+  ) : super(
+          parent,
+          parentInvocation,
+        );
+}
+
+class _FakeOffset_2 extends _i1.SmartFake implements _i3.Offset {
+  _FakeOffset_2(
+    Object parent,
+    Invocation parentInvocation,
+  ) : super(
+          parent,
+          parentInvocation,
+        );
+}
+
+class _FakeWebView_3 extends _i1.SmartFake implements _i2.WebView {
+  _FakeWebView_3(
+    Object parent,
+    Invocation parentInvocation,
+  ) : super(
+          parent,
+          parentInvocation,
+        );
+}
+
+class _FakeDownloadListener_4 extends _i1.SmartFake
+    implements _i2.DownloadListener {
+  _FakeDownloadListener_4(
+    Object parent,
+    Invocation parentInvocation,
+  ) : super(
+          parent,
+          parentInvocation,
+        );
+}
+
+class _FakeJavascriptChannelRegistry_5 extends _i1.SmartFake
+    implements _i4.JavascriptChannelRegistry {
+  _FakeJavascriptChannelRegistry_5(
+    Object parent,
+    Invocation parentInvocation,
+  ) : super(
+          parent,
+          parentInvocation,
+        );
+}
+
+class _FakeJavaScriptChannel_6 extends _i1.SmartFake
+    implements _i2.JavaScriptChannel {
+  _FakeJavaScriptChannel_6(
+    Object parent,
+    Invocation parentInvocation,
+  ) : super(
+          parent,
+          parentInvocation,
+        );
+}
+
+class _FakeWebChromeClient_7 extends _i1.SmartFake
+    implements _i2.WebChromeClient {
+  _FakeWebChromeClient_7(
+    Object parent,
+    Invocation parentInvocation,
+  ) : super(
+          parent,
+          parentInvocation,
+        );
+}
+
+class _FakeWebViewClient_8 extends _i1.SmartFake implements _i2.WebViewClient {
+  _FakeWebViewClient_8(
+    Object parent,
+    Invocation parentInvocation,
+  ) : super(
+          parent,
+          parentInvocation,
+        );
+}
+
+/// A class which mocks [FlutterAssetManager].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockFlutterAssetManager extends _i1.Mock
+    implements _i2.FlutterAssetManager {
+  MockFlutterAssetManager() {
+    _i1.throwOnMissingStub(this);
+  }
+
+  @override
+  _i5.Future<List<String?>> list(String? path) => (super.noSuchMethod(
+        Invocation.method(
+          #list,
+          [path],
+        ),
+        returnValue: _i5.Future<List<String?>>.value(<String?>[]),
+      ) as _i5.Future<List<String?>>);
+  @override
+  _i5.Future<String> getAssetFilePathByName(String? name) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #getAssetFilePathByName,
+          [name],
+        ),
+        returnValue: _i5.Future<String>.value(''),
+      ) as _i5.Future<String>);
+}
+
+/// A class which mocks [WebSettings].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockWebSettings extends _i1.Mock implements _i2.WebSettings {
+  MockWebSettings() {
+    _i1.throwOnMissingStub(this);
+  }
+
+  @override
+  _i5.Future<void> setDomStorageEnabled(bool? flag) => (super.noSuchMethod(
+        Invocation.method(
+          #setDomStorageEnabled,
+          [flag],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
+  @override
+  _i5.Future<void> setJavaScriptCanOpenWindowsAutomatically(bool? flag) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #setJavaScriptCanOpenWindowsAutomatically,
+          [flag],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
+  @override
+  _i5.Future<void> setSupportMultipleWindows(bool? support) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #setSupportMultipleWindows,
+          [support],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
+  @override
+  _i5.Future<void> setJavaScriptEnabled(bool? flag) => (super.noSuchMethod(
+        Invocation.method(
+          #setJavaScriptEnabled,
+          [flag],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
+  @override
+  _i5.Future<void> setUserAgentString(String? userAgentString) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #setUserAgentString,
+          [userAgentString],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
+  @override
+  _i5.Future<void> setMediaPlaybackRequiresUserGesture(bool? require) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #setMediaPlaybackRequiresUserGesture,
+          [require],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
+  @override
+  _i5.Future<void> setSupportZoom(bool? support) => (super.noSuchMethod(
+        Invocation.method(
+          #setSupportZoom,
+          [support],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
+  @override
+  _i5.Future<void> setLoadWithOverviewMode(bool? overview) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #setLoadWithOverviewMode,
+          [overview],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
+  @override
+  _i5.Future<void> setUseWideViewPort(bool? use) => (super.noSuchMethod(
+        Invocation.method(
+          #setUseWideViewPort,
+          [use],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
+  @override
+  _i5.Future<void> setDisplayZoomControls(bool? enabled) => (super.noSuchMethod(
+        Invocation.method(
+          #setDisplayZoomControls,
+          [enabled],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
+  @override
+  _i5.Future<void> setBuiltInZoomControls(bool? enabled) => (super.noSuchMethod(
+        Invocation.method(
+          #setBuiltInZoomControls,
+          [enabled],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
+  @override
+  _i5.Future<void> setAllowFileAccess(bool? enabled) => (super.noSuchMethod(
+        Invocation.method(
+          #setAllowFileAccess,
+          [enabled],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
+  @override
+  _i2.WebSettings copy() => (super.noSuchMethod(
+        Invocation.method(
+          #copy,
+          [],
+        ),
+        returnValue: _FakeWebSettings_0(
+          this,
+          Invocation.method(
+            #copy,
+            [],
+          ),
+        ),
+      ) as _i2.WebSettings);
+}
+
+/// A class which mocks [WebStorage].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockWebStorage extends _i1.Mock implements _i2.WebStorage {
+  MockWebStorage() {
+    _i1.throwOnMissingStub(this);
+  }
+
+  @override
+  _i5.Future<void> deleteAllData() => (super.noSuchMethod(
+        Invocation.method(
+          #deleteAllData,
+          [],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
+  @override
+  _i2.WebStorage copy() => (super.noSuchMethod(
+        Invocation.method(
+          #copy,
+          [],
+        ),
+        returnValue: _FakeWebStorage_1(
+          this,
+          Invocation.method(
+            #copy,
+            [],
+          ),
+        ),
+      ) as _i2.WebStorage);
+}
+
+/// A class which mocks [WebView].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockWebView extends _i1.Mock implements _i2.WebView {
+  MockWebView() {
+    _i1.throwOnMissingStub(this);
+  }
+
+  @override
+  bool get useHybridComposition => (super.noSuchMethod(
+        Invocation.getter(#useHybridComposition),
+        returnValue: false,
+      ) as bool);
+  @override
+  _i2.WebSettings get settings => (super.noSuchMethod(
+        Invocation.getter(#settings),
+        returnValue: _FakeWebSettings_0(
+          this,
+          Invocation.getter(#settings),
+        ),
+      ) as _i2.WebSettings);
+  @override
+  _i5.Future<void> loadData({
+    required String? data,
+    String? mimeType,
+    String? encoding,
+  }) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #loadData,
+          [],
+          {
+            #data: data,
+            #mimeType: mimeType,
+            #encoding: encoding,
+          },
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
+  @override
+  _i5.Future<void> loadDataWithBaseUrl({
+    String? baseUrl,
+    required String? data,
+    String? mimeType,
+    String? encoding,
+    String? historyUrl,
+  }) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #loadDataWithBaseUrl,
+          [],
+          {
+            #baseUrl: baseUrl,
+            #data: data,
+            #mimeType: mimeType,
+            #encoding: encoding,
+            #historyUrl: historyUrl,
+          },
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
+  @override
+  _i5.Future<void> loadUrl(
+    String? url,
+    Map<String, String>? headers,
+  ) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #loadUrl,
+          [
+            url,
+            headers,
+          ],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
+  @override
+  _i5.Future<void> postUrl(
+    String? url,
+    _i6.Uint8List? data,
+  ) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #postUrl,
+          [
+            url,
+            data,
+          ],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
+  @override
+  _i5.Future<String?> getUrl() => (super.noSuchMethod(
+        Invocation.method(
+          #getUrl,
+          [],
+        ),
+        returnValue: _i5.Future<String?>.value(),
+      ) as _i5.Future<String?>);
+  @override
+  _i5.Future<bool> canGoBack() => (super.noSuchMethod(
+        Invocation.method(
+          #canGoBack,
+          [],
+        ),
+        returnValue: _i5.Future<bool>.value(false),
+      ) as _i5.Future<bool>);
+  @override
+  _i5.Future<bool> canGoForward() => (super.noSuchMethod(
+        Invocation.method(
+          #canGoForward,
+          [],
+        ),
+        returnValue: _i5.Future<bool>.value(false),
+      ) as _i5.Future<bool>);
+  @override
+  _i5.Future<void> goBack() => (super.noSuchMethod(
+        Invocation.method(
+          #goBack,
+          [],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
+  @override
+  _i5.Future<void> goForward() => (super.noSuchMethod(
+        Invocation.method(
+          #goForward,
+          [],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
+  @override
+  _i5.Future<void> reload() => (super.noSuchMethod(
+        Invocation.method(
+          #reload,
+          [],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
+  @override
+  _i5.Future<void> clearCache(bool? includeDiskFiles) => (super.noSuchMethod(
+        Invocation.method(
+          #clearCache,
+          [includeDiskFiles],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
+  @override
+  _i5.Future<String?> evaluateJavascript(String? javascriptString) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #evaluateJavascript,
+          [javascriptString],
+        ),
+        returnValue: _i5.Future<String?>.value(),
+      ) as _i5.Future<String?>);
+  @override
+  _i5.Future<String?> getTitle() => (super.noSuchMethod(
+        Invocation.method(
+          #getTitle,
+          [],
+        ),
+        returnValue: _i5.Future<String?>.value(),
+      ) as _i5.Future<String?>);
+  @override
+  _i5.Future<void> scrollTo(
+    int? x,
+    int? y,
+  ) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #scrollTo,
+          [
+            x,
+            y,
+          ],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
+  @override
+  _i5.Future<void> scrollBy(
+    int? x,
+    int? y,
+  ) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #scrollBy,
+          [
+            x,
+            y,
+          ],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
+  @override
+  _i5.Future<int> getScrollX() => (super.noSuchMethod(
+        Invocation.method(
+          #getScrollX,
+          [],
+        ),
+        returnValue: _i5.Future<int>.value(0),
+      ) as _i5.Future<int>);
+  @override
+  _i5.Future<int> getScrollY() => (super.noSuchMethod(
+        Invocation.method(
+          #getScrollY,
+          [],
+        ),
+        returnValue: _i5.Future<int>.value(0),
+      ) as _i5.Future<int>);
+  @override
+  _i5.Future<_i3.Offset> getScrollPosition() => (super.noSuchMethod(
+        Invocation.method(
+          #getScrollPosition,
+          [],
+        ),
+        returnValue: _i5.Future<_i3.Offset>.value(_FakeOffset_2(
+          this,
+          Invocation.method(
+            #getScrollPosition,
+            [],
+          ),
+        )),
+      ) as _i5.Future<_i3.Offset>);
+  @override
+  _i5.Future<void> setWebViewClient(_i2.WebViewClient? webViewClient) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #setWebViewClient,
+          [webViewClient],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
+  @override
+  _i5.Future<void> addJavaScriptChannel(
+          _i2.JavaScriptChannel? javaScriptChannel) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #addJavaScriptChannel,
+          [javaScriptChannel],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
+  @override
+  _i5.Future<void> removeJavaScriptChannel(
+          _i2.JavaScriptChannel? javaScriptChannel) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #removeJavaScriptChannel,
+          [javaScriptChannel],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
+  @override
+  _i5.Future<void> setDownloadListener(_i2.DownloadListener? listener) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #setDownloadListener,
+          [listener],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
+  @override
+  _i5.Future<void> setWebChromeClient(_i2.WebChromeClient? client) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #setWebChromeClient,
+          [client],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
+  @override
+  _i5.Future<void> setBackgroundColor(_i3.Color? color) => (super.noSuchMethod(
+        Invocation.method(
+          #setBackgroundColor,
+          [color],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
+  @override
+  _i2.WebView copy() => (super.noSuchMethod(
+        Invocation.method(
+          #copy,
+          [],
+        ),
+        returnValue: _FakeWebView_3(
+          this,
+          Invocation.method(
+            #copy,
+            [],
+          ),
+        ),
+      ) as _i2.WebView);
+}
+
+/// A class which mocks [WebResourceRequest].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockWebResourceRequest extends _i1.Mock
+    implements _i2.WebResourceRequest {
+  MockWebResourceRequest() {
+    _i1.throwOnMissingStub(this);
+  }
+
+  @override
+  String get url => (super.noSuchMethod(
+        Invocation.getter(#url),
+        returnValue: '',
+      ) as String);
+  @override
+  bool get isForMainFrame => (super.noSuchMethod(
+        Invocation.getter(#isForMainFrame),
+        returnValue: false,
+      ) as bool);
+  @override
+  bool get hasGesture => (super.noSuchMethod(
+        Invocation.getter(#hasGesture),
+        returnValue: false,
+      ) as bool);
+  @override
+  String get method => (super.noSuchMethod(
+        Invocation.getter(#method),
+        returnValue: '',
+      ) as String);
+  @override
+  Map<String, String> get requestHeaders => (super.noSuchMethod(
+        Invocation.getter(#requestHeaders),
+        returnValue: <String, String>{},
+      ) as Map<String, String>);
+}
+
+/// A class which mocks [DownloadListener].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockDownloadListener extends _i1.Mock implements _i2.DownloadListener {
+  MockDownloadListener() {
+    _i1.throwOnMissingStub(this);
+  }
+
+  @override
+  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,
+          [],
+        ),
+        returnValue: _FakeDownloadListener_4(
+          this,
+          Invocation.method(
+            #copy,
+            [],
+          ),
+        ),
+      ) as _i2.DownloadListener);
+}
+
+/// A class which mocks [WebViewAndroidJavaScriptChannel].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockWebViewAndroidJavaScriptChannel extends _i1.Mock
+    implements _i7.WebViewAndroidJavaScriptChannel {
+  MockWebViewAndroidJavaScriptChannel() {
+    _i1.throwOnMissingStub(this);
+  }
+
+  @override
+  _i4.JavascriptChannelRegistry get javascriptChannelRegistry =>
+      (super.noSuchMethod(
+        Invocation.getter(#javascriptChannelRegistry),
+        returnValue: _FakeJavascriptChannelRegistry_5(
+          this,
+          Invocation.getter(#javascriptChannelRegistry),
+        ),
+      ) as _i4.JavascriptChannelRegistry);
+  @override
+  String get channelName => (super.noSuchMethod(
+        Invocation.getter(#channelName),
+        returnValue: '',
+      ) as String);
+  @override
+  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(
+          this,
+          Invocation.method(
+            #copy,
+            [],
+          ),
+        ),
+      ) as _i2.JavaScriptChannel);
+}
+
+/// A class which mocks [WebChromeClient].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockWebChromeClient extends _i1.Mock implements _i2.WebChromeClient {
+  MockWebChromeClient() {
+    _i1.throwOnMissingStub(this);
+  }
+
+  @override
+  _i2.WebChromeClient copy() => (super.noSuchMethod(
+        Invocation.method(
+          #copy,
+          [],
+        ),
+        returnValue: _FakeWebChromeClient_7(
+          this,
+          Invocation.method(
+            #copy,
+            [],
+          ),
+        ),
+      ) as _i2.WebChromeClient);
+}
+
+/// A class which mocks [WebViewClient].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockWebViewClient extends _i1.Mock implements _i2.WebViewClient {
+  MockWebViewClient() {
+    _i1.throwOnMissingStub(this);
+  }
+
+  @override
+  _i5.Future<void> setSynchronousReturnValueForShouldOverrideUrlLoading(
+          bool? value) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #setSynchronousReturnValueForShouldOverrideUrlLoading,
+          [value],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
+  @override
+  _i2.WebViewClient copy() => (super.noSuchMethod(
+        Invocation.method(
+          #copy,
+          [],
+        ),
+        returnValue: _FakeWebViewClient_8(
+          this,
+          Invocation.method(
+            #copy,
+            [],
+          ),
+        ),
+      ) as _i2.WebViewClient);
+}
+
+/// A class which mocks [JavascriptChannelRegistry].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockJavascriptChannelRegistry extends _i1.Mock
+    implements _i4.JavascriptChannelRegistry {
+  MockJavascriptChannelRegistry() {
+    _i1.throwOnMissingStub(this);
+  }
+
+  @override
+  Map<String, _i4.JavascriptChannel> get channels => (super.noSuchMethod(
+        Invocation.getter(#channels),
+        returnValue: <String, _i4.JavascriptChannel>{},
+      ) as Map<String, _i4.JavascriptChannel>);
+  @override
+  void onJavascriptChannelMessage(
+    String? channel,
+    String? message,
+  ) =>
+      super.noSuchMethod(
+        Invocation.method(
+          #onJavascriptChannelMessage,
+          [
+            channel,
+            message,
+          ],
+        ),
+        returnValueForMissingStub: null,
+      );
+  @override
+  void updateJavascriptChannelsFromSet(Set<_i4.JavascriptChannel>? channels) =>
+      super.noSuchMethod(
+        Invocation.method(
+          #updateJavascriptChannelsFromSet,
+          [channels],
+        ),
+        returnValueForMissingStub: null,
+      );
+}
+
+/// A class which mocks [WebViewPlatformCallbacksHandler].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockWebViewPlatformCallbacksHandler extends _i1.Mock
+    implements _i4.WebViewPlatformCallbacksHandler {
+  MockWebViewPlatformCallbacksHandler() {
+    _i1.throwOnMissingStub(this);
+  }
+
+  @override
+  _i5.FutureOr<bool> onNavigationRequest({
+    required String? url,
+    required bool? isForMainFrame,
+  }) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #onNavigationRequest,
+          [],
+          {
+            #url: url,
+            #isForMainFrame: isForMainFrame,
+          },
+        ),
+        returnValue: _i5.Future<bool>.value(false),
+      ) as _i5.FutureOr<bool>);
+  @override
+  void onPageStarted(String? url) => super.noSuchMethod(
+        Invocation.method(
+          #onPageStarted,
+          [url],
+        ),
+        returnValueForMissingStub: null,
+      );
+  @override
+  void onPageFinished(String? url) => super.noSuchMethod(
+        Invocation.method(
+          #onPageFinished,
+          [url],
+        ),
+        returnValueForMissingStub: null,
+      );
+  @override
+  void onProgress(int? progress) => super.noSuchMethod(
+        Invocation.method(
+          #onProgress,
+          [progress],
+        ),
+        returnValueForMissingStub: null,
+      );
+  @override
+  void onWebResourceError(_i4.WebResourceError? error) => super.noSuchMethod(
+        Invocation.method(
+          #onWebResourceError,
+          [error],
+        ),
+        returnValueForMissingStub: null,
+      );
+}
+
+/// A class which mocks [WebViewProxy].
+///
+/// See the documentation for Mockito's code generation for more information.
+class MockWebViewProxy extends _i1.Mock implements _i7.WebViewProxy {
+  MockWebViewProxy() {
+    _i1.throwOnMissingStub(this);
+  }
+
+  @override
+  _i2.WebView createWebView({required bool? useHybridComposition}) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #createWebView,
+          [],
+          {#useHybridComposition: useHybridComposition},
+        ),
+        returnValue: _FakeWebView_3(
+          this,
+          Invocation.method(
+            #createWebView,
+            [],
+            {#useHybridComposition: useHybridComposition},
+          ),
+        ),
+      ) as _i2.WebView);
+  @override
+  _i2.WebViewClient createWebViewClient({
+    void Function(
+      _i2.WebView,
+      String,
+    )?
+        onPageStarted,
+    void Function(
+      _i2.WebView,
+      String,
+    )?
+        onPageFinished,
+    void Function(
+      _i2.WebView,
+      _i2.WebResourceRequest,
+      _i2.WebResourceError,
+    )?
+        onReceivedRequestError,
+    void Function(
+      _i2.WebView,
+      int,
+      String,
+      String,
+    )?
+        onReceivedError,
+    void Function(
+      _i2.WebView,
+      _i2.WebResourceRequest,
+    )?
+        requestLoading,
+    void Function(
+      _i2.WebView,
+      String,
+    )?
+        urlLoading,
+  }) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #createWebViewClient,
+          [],
+          {
+            #onPageStarted: onPageStarted,
+            #onPageFinished: onPageFinished,
+            #onReceivedRequestError: onReceivedRequestError,
+            #onReceivedError: onReceivedError,
+            #requestLoading: requestLoading,
+            #urlLoading: urlLoading,
+          },
+        ),
+        returnValue: _FakeWebViewClient_8(
+          this,
+          Invocation.method(
+            #createWebViewClient,
+            [],
+            {
+              #onPageStarted: onPageStarted,
+              #onPageFinished: onPageFinished,
+              #onReceivedRequestError: onReceivedRequestError,
+              #onReceivedError: onReceivedError,
+              #requestLoading: requestLoading,
+              #urlLoading: urlLoading,
+            },
+          ),
+        ),
+      ) as _i2.WebViewClient);
+  @override
+  _i5.Future<void> setWebContentsDebuggingEnabled(bool? enabled) =>
+      (super.noSuchMethod(
+        Invocation.method(
+          #setWebContentsDebuggingEnabled,
+          [enabled],
+        ),
+        returnValue: _i5.Future<void>.value(),
+        returnValueForMissingStub: _i5.Future<void>.value(),
+      ) as _i5.Future<void>);
+}
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..bcfb453 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
@@ -1,7 +1,7 @@
 // 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.
-// Autogenerated from Pigeon (v4.0.2), do not edit directly.
+// Autogenerated from Pigeon (v4.2.3), do not edit directly.
 // See also: https://pub.dev/packages/pigeon
 // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, unnecessary_import
 // ignore_for_file: avoid_relative_lib_imports
@@ -13,12 +13,13 @@
 
 import 'package:webview_flutter_android/src/android_webview.pigeon.dart';
 
-class _TestJavaObjectHostApiCodec extends StandardMessageCodec {
-  const _TestJavaObjectHostApiCodec();
-}
-
+/// Handles methods calls to the native Java Object class.
+///
+/// Also handles calls to remove the reference to an instance with `dispose`.
+///
+/// See https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html.
 abstract class TestJavaObjectHostApi {
-  static const MessageCodec<Object?> codec = _TestJavaObjectHostApiCodec();
+  static const MessageCodec<Object?> codec = StandardMessageCodec();
 
   void dispose(int identifier);
   static void setup(TestJavaObjectHostApi? api,
@@ -73,7 +74,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 +127,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) {
@@ -684,15 +665,10 @@
   }
 }
 
-class _TestWebSettingsHostApiCodec extends StandardMessageCodec {
-  const _TestWebSettingsHostApiCodec();
-}
-
 abstract class TestWebSettingsHostApi {
-  static const MessageCodec<Object?> codec = _TestWebSettingsHostApiCodec();
+  static const MessageCodec<Object?> codec = StandardMessageCodec();
 
   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 +707,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) {
@@ -1019,13 +976,8 @@
   }
 }
 
-class _TestJavaScriptChannelHostApiCodec extends StandardMessageCodec {
-  const _TestJavaScriptChannelHostApiCodec();
-}
-
 abstract class TestJavaScriptChannelHostApi {
-  static const MessageCodec<Object?> codec =
-      _TestJavaScriptChannelHostApiCodec();
+  static const MessageCodec<Object?> codec = StandardMessageCodec();
 
   void create(int instanceId, String channelName);
   static void setup(TestJavaScriptChannelHostApi? api,
@@ -1055,14 +1007,12 @@
   }
 }
 
-class _TestWebViewClientHostApiCodec extends StandardMessageCodec {
-  const _TestWebViewClientHostApiCodec();
-}
-
 abstract class TestWebViewClientHostApi {
-  static const MessageCodec<Object?> codec = _TestWebViewClientHostApiCodec();
+  static const MessageCodec<Object?> codec = StandardMessageCodec();
 
-  void create(int instanceId, bool shouldOverrideUrlLoading);
+  void create(int instanceId);
+  void setSynchronousReturnValueForShouldOverrideUrlLoading(
+      int instanceId, bool value);
   static void setup(TestWebViewClientHostApi? api,
       {BinaryMessenger? binaryMessenger}) {
     {
@@ -1079,10 +1029,31 @@
           final int? arg_instanceId = (args[0] as int?);
           assert(arg_instanceId != null,
               'Argument for dev.flutter.pigeon.WebViewClientHostApi.create was null, expected non-null int.');
-          final bool? arg_shouldOverrideUrlLoading = (args[1] as bool?);
-          assert(arg_shouldOverrideUrlLoading != null,
-              'Argument for dev.flutter.pigeon.WebViewClientHostApi.create was null, expected non-null bool.');
-          api.create(arg_instanceId!, arg_shouldOverrideUrlLoading!);
+          api.create(arg_instanceId!);
+          return <Object?, Object?>{};
+        });
+      }
+    }
+    {
+      final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+          'dev.flutter.pigeon.WebViewClientHostApi.setSynchronousReturnValueForShouldOverrideUrlLoading',
+          codec,
+          binaryMessenger: binaryMessenger);
+      if (api == null) {
+        channel.setMockMessageHandler(null);
+      } else {
+        channel.setMockMessageHandler((Object? message) async {
+          assert(message != null,
+              'Argument for dev.flutter.pigeon.WebViewClientHostApi.setSynchronousReturnValueForShouldOverrideUrlLoading 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.WebViewClientHostApi.setSynchronousReturnValueForShouldOverrideUrlLoading was null, expected non-null int.');
+          final bool? arg_value = (args[1] as bool?);
+          assert(arg_value != null,
+              'Argument for dev.flutter.pigeon.WebViewClientHostApi.setSynchronousReturnValueForShouldOverrideUrlLoading was null, expected non-null bool.');
+          api.setSynchronousReturnValueForShouldOverrideUrlLoading(
+              arg_instanceId!, arg_value!);
           return <Object?, Object?>{};
         });
       }
@@ -1090,13 +1061,8 @@
   }
 }
 
-class _TestDownloadListenerHostApiCodec extends StandardMessageCodec {
-  const _TestDownloadListenerHostApiCodec();
-}
-
 abstract class TestDownloadListenerHostApi {
-  static const MessageCodec<Object?> codec =
-      _TestDownloadListenerHostApiCodec();
+  static const MessageCodec<Object?> codec = StandardMessageCodec();
 
   void create(int instanceId);
   static void setup(TestDownloadListenerHostApi? api,
@@ -1123,14 +1089,10 @@
   }
 }
 
-class _TestWebChromeClientHostApiCodec extends StandardMessageCodec {
-  const _TestWebChromeClientHostApiCodec();
-}
-
 abstract class TestWebChromeClientHostApi {
-  static const MessageCodec<Object?> codec = _TestWebChromeClientHostApiCodec();
+  static const MessageCodec<Object?> codec = StandardMessageCodec();
 
-  void create(int instanceId, int webViewClientInstanceId);
+  void create(int instanceId);
   static void setup(TestWebChromeClientHostApi? api,
       {BinaryMessenger? binaryMessenger}) {
     {
@@ -1147,10 +1109,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?>{};
         });
       }
@@ -1158,12 +1117,8 @@
   }
 }
 
-class _TestAssetManagerHostApiCodec extends StandardMessageCodec {
-  const _TestAssetManagerHostApiCodec();
-}
-
 abstract class TestAssetManagerHostApi {
-  static const MessageCodec<Object?> codec = _TestAssetManagerHostApiCodec();
+  static const MessageCodec<Object?> codec = StandardMessageCodec();
 
   List<String?> list(String path);
   String getAssetFilePathByName(String name);
@@ -1211,12 +1166,8 @@
   }
 }
 
-class _TestWebStorageHostApiCodec extends StandardMessageCodec {
-  const _TestWebStorageHostApiCodec();
-}
-
 abstract class TestWebStorageHostApi {
-  static const MessageCodec<Object?> codec = _TestWebStorageHostApiCodec();
+  static const MessageCodec<Object?> codec = StandardMessageCodec();
 
   void create(int instanceId);
   void deleteAllData(int instanceId);
diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_cookie_manager_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_cookie_manager_test.mocks.dart
deleted file mode 100644
index 308aba4..0000000
--- a/packages/webview_flutter/webview_flutter_android/test/webview_android_cookie_manager_test.mocks.dart
+++ /dev/null
@@ -1,37 +0,0 @@
-// Mocks generated by Mockito 5.2.0 from annotations
-// in webview_flutter_android/test/webview_android_cookie_manager_test.dart.
-// Do not manually edit this file.
-
-import 'dart:async' as _i3;
-
-import 'package:mockito/mockito.dart' as _i1;
-import 'package:webview_flutter_android/src/android_webview.dart' as _i2;
-
-// ignore_for_file: type=lint
-// ignore_for_file: avoid_redundant_argument_values
-// ignore_for_file: avoid_setters_without_getters
-// ignore_for_file: comment_references
-// ignore_for_file: implementation_imports
-// ignore_for_file: invalid_use_of_visible_for_testing_member
-// ignore_for_file: prefer_const_constructors
-// ignore_for_file: unnecessary_parenthesis
-// ignore_for_file: camel_case_types
-
-/// A class which mocks [CookieManager].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockCookieManager extends _i1.Mock implements _i2.CookieManager {
-  MockCookieManager() {
-    _i1.throwOnMissingStub(this);
-  }
-
-  @override
-  _i3.Future<void> setCookie(String? url, String? value) =>
-      (super.noSuchMethod(Invocation.method(#setCookie, [url, value]),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i3.Future<void>);
-  @override
-  _i3.Future<bool> clearCookies() =>
-      (super.noSuchMethod(Invocation.method(#clearCookies, []),
-          returnValue: Future<bool>.value(false)) as _i3.Future<bool>);
-}
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
deleted file mode 100644
index acaff1c..0000000
--- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart
+++ /dev/null
@@ -1,572 +0,0 @@
-// Mocks generated by Mockito 5.2.0 from annotations
-// in webview_flutter_android/test/webview_android_widget_test.dart.
-// Do not manually edit this file.
-
-import 'dart:async' as _i5;
-import 'dart:typed_data' as _i6;
-import 'dart:ui' as _i3;
-
-import 'package:mockito/mockito.dart' as _i1;
-import 'package:webview_flutter_android/src/android_webview.dart' as _i2;
-import 'package:webview_flutter_android/webview_android_widget.dart' as _i7;
-import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'
-    as _i4;
-
-// ignore_for_file: type=lint
-// ignore_for_file: avoid_redundant_argument_values
-// ignore_for_file: avoid_setters_without_getters
-// ignore_for_file: comment_references
-// ignore_for_file: implementation_imports
-// ignore_for_file: invalid_use_of_visible_for_testing_member
-// ignore_for_file: prefer_const_constructors
-// ignore_for_file: unnecessary_parenthesis
-// ignore_for_file: camel_case_types
-
-class _FakeWebSettings_0 extends _i1.Fake implements _i2.WebSettings {}
-
-class _FakeWebStorage_1 extends _i1.Fake implements _i2.WebStorage {}
-
-class _FakeOffset_2 extends _i1.Fake implements _i3.Offset {}
-
-class _FakeWebView_3 extends _i1.Fake implements _i2.WebView {}
-
-class _FakeDownloadListener_4 extends _i1.Fake
-    implements _i2.DownloadListener {}
-
-class _FakeJavascriptChannelRegistry_5 extends _i1.Fake
-    implements _i4.JavascriptChannelRegistry {}
-
-class _FakeJavaScriptChannel_6 extends _i1.Fake
-    implements _i2.JavaScriptChannel {}
-
-class _FakeWebChromeClient_7 extends _i1.Fake implements _i2.WebChromeClient {}
-
-class _FakeWebViewClient_8 extends _i1.Fake implements _i2.WebViewClient {}
-
-/// A class which mocks [FlutterAssetManager].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockFlutterAssetManager extends _i1.Mock
-    implements _i2.FlutterAssetManager {
-  MockFlutterAssetManager() {
-    _i1.throwOnMissingStub(this);
-  }
-
-  @override
-  _i5.Future<List<String?>> list(String? path) =>
-      (super.noSuchMethod(Invocation.method(#list, [path]),
-              returnValue: Future<List<String?>>.value(<String?>[]))
-          as _i5.Future<List<String?>>);
-  @override
-  _i5.Future<String> getAssetFilePathByName(String? name) =>
-      (super.noSuchMethod(Invocation.method(#getAssetFilePathByName, [name]),
-          returnValue: Future<String>.value('')) as _i5.Future<String>);
-}
-
-/// A class which mocks [WebSettings].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockWebSettings extends _i1.Mock implements _i2.WebSettings {
-  MockWebSettings() {
-    _i1.throwOnMissingStub(this);
-  }
-
-  @override
-  _i5.Future<void> setDomStorageEnabled(bool? flag) =>
-      (super.noSuchMethod(Invocation.method(#setDomStorageEnabled, [flag]),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
-  @override
-  _i5.Future<void> setJavaScriptCanOpenWindowsAutomatically(bool? flag) =>
-      (super.noSuchMethod(
-          Invocation.method(#setJavaScriptCanOpenWindowsAutomatically, [flag]),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
-  @override
-  _i5.Future<void> setSupportMultipleWindows(bool? support) => (super
-      .noSuchMethod(Invocation.method(#setSupportMultipleWindows, [support]),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
-  @override
-  _i5.Future<void> setJavaScriptEnabled(bool? flag) =>
-      (super.noSuchMethod(Invocation.method(#setJavaScriptEnabled, [flag]),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
-  @override
-  _i5.Future<void> setUserAgentString(String? userAgentString) => (super
-      .noSuchMethod(Invocation.method(#setUserAgentString, [userAgentString]),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
-  @override
-  _i5.Future<void> setMediaPlaybackRequiresUserGesture(bool? require) =>
-      (super.noSuchMethod(
-          Invocation.method(#setMediaPlaybackRequiresUserGesture, [require]),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
-  @override
-  _i5.Future<void> setSupportZoom(bool? support) =>
-      (super.noSuchMethod(Invocation.method(#setSupportZoom, [support]),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
-  @override
-  _i5.Future<void> setLoadWithOverviewMode(bool? overview) => (super
-      .noSuchMethod(Invocation.method(#setLoadWithOverviewMode, [overview]),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
-  @override
-  _i5.Future<void> setUseWideViewPort(bool? use) =>
-      (super.noSuchMethod(Invocation.method(#setUseWideViewPort, [use]),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
-  @override
-  _i5.Future<void> setDisplayZoomControls(bool? enabled) =>
-      (super.noSuchMethod(Invocation.method(#setDisplayZoomControls, [enabled]),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
-  @override
-  _i5.Future<void> setBuiltInZoomControls(bool? enabled) =>
-      (super.noSuchMethod(Invocation.method(#setBuiltInZoomControls, [enabled]),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
-  @override
-  _i5.Future<void> setAllowFileAccess(bool? enabled) =>
-      (super.noSuchMethod(Invocation.method(#setAllowFileAccess, [enabled]),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
-  @override
-  _i2.WebSettings copy() => (super.noSuchMethod(Invocation.method(#copy, []),
-      returnValue: _FakeWebSettings_0()) as _i2.WebSettings);
-}
-
-/// A class which mocks [WebStorage].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockWebStorage extends _i1.Mock implements _i2.WebStorage {
-  MockWebStorage() {
-    _i1.throwOnMissingStub(this);
-  }
-
-  @override
-  _i5.Future<void> deleteAllData() =>
-      (super.noSuchMethod(Invocation.method(#deleteAllData, []),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
-  @override
-  _i2.WebStorage copy() => (super.noSuchMethod(Invocation.method(#copy, []),
-      returnValue: _FakeWebStorage_1()) as _i2.WebStorage);
-}
-
-/// A class which mocks [WebView].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockWebView extends _i1.Mock implements _i2.WebView {
-  MockWebView() {
-    _i1.throwOnMissingStub(this);
-  }
-
-  @override
-  bool get useHybridComposition =>
-      (super.noSuchMethod(Invocation.getter(#useHybridComposition),
-          returnValue: false) as bool);
-  @override
-  _i2.WebSettings get settings =>
-      (super.noSuchMethod(Invocation.getter(#settings),
-          returnValue: _FakeWebSettings_0()) as _i2.WebSettings);
-  @override
-  _i5.Future<void> loadData(
-          {String? data, String? mimeType, String? encoding}) =>
-      (super.noSuchMethod(
-          Invocation.method(#loadData, [],
-              {#data: data, #mimeType: mimeType, #encoding: encoding}),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
-  @override
-  _i5.Future<void> loadDataWithBaseUrl(
-          {String? baseUrl,
-          String? data,
-          String? mimeType,
-          String? encoding,
-          String? historyUrl}) =>
-      (super.noSuchMethod(
-          Invocation.method(#loadDataWithBaseUrl, [], {
-            #baseUrl: baseUrl,
-            #data: data,
-            #mimeType: mimeType,
-            #encoding: encoding,
-            #historyUrl: historyUrl
-          }),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
-  @override
-  _i5.Future<void> loadUrl(String? url, Map<String, String>? headers) =>
-      (super.noSuchMethod(Invocation.method(#loadUrl, [url, headers]),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
-  @override
-  _i5.Future<void> postUrl(String? url, _i6.Uint8List? data) =>
-      (super.noSuchMethod(Invocation.method(#postUrl, [url, data]),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
-  @override
-  _i5.Future<String?> getUrl() =>
-      (super.noSuchMethod(Invocation.method(#getUrl, []),
-          returnValue: Future<String?>.value()) as _i5.Future<String?>);
-  @override
-  _i5.Future<bool> canGoBack() =>
-      (super.noSuchMethod(Invocation.method(#canGoBack, []),
-          returnValue: Future<bool>.value(false)) as _i5.Future<bool>);
-  @override
-  _i5.Future<bool> canGoForward() =>
-      (super.noSuchMethod(Invocation.method(#canGoForward, []),
-          returnValue: Future<bool>.value(false)) as _i5.Future<bool>);
-  @override
-  _i5.Future<void> goBack() =>
-      (super.noSuchMethod(Invocation.method(#goBack, []),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
-  @override
-  _i5.Future<void> goForward() =>
-      (super.noSuchMethod(Invocation.method(#goForward, []),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
-  @override
-  _i5.Future<void> reload() =>
-      (super.noSuchMethod(Invocation.method(#reload, []),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
-  @override
-  _i5.Future<void> clearCache(bool? includeDiskFiles) =>
-      (super.noSuchMethod(Invocation.method(#clearCache, [includeDiskFiles]),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
-  @override
-  _i5.Future<String?> evaluateJavascript(String? javascriptString) => (super
-      .noSuchMethod(Invocation.method(#evaluateJavascript, [javascriptString]),
-          returnValue: Future<String?>.value()) as _i5.Future<String?>);
-  @override
-  _i5.Future<String?> getTitle() =>
-      (super.noSuchMethod(Invocation.method(#getTitle, []),
-          returnValue: Future<String?>.value()) as _i5.Future<String?>);
-  @override
-  _i5.Future<void> scrollTo(int? x, int? y) =>
-      (super.noSuchMethod(Invocation.method(#scrollTo, [x, y]),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
-  @override
-  _i5.Future<void> scrollBy(int? x, int? y) =>
-      (super.noSuchMethod(Invocation.method(#scrollBy, [x, y]),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
-  @override
-  _i5.Future<int> getScrollX() =>
-      (super.noSuchMethod(Invocation.method(#getScrollX, []),
-          returnValue: Future<int>.value(0)) as _i5.Future<int>);
-  @override
-  _i5.Future<int> getScrollY() =>
-      (super.noSuchMethod(Invocation.method(#getScrollY, []),
-          returnValue: Future<int>.value(0)) as _i5.Future<int>);
-  @override
-  _i5.Future<_i3.Offset> getScrollPosition() =>
-      (super.noSuchMethod(Invocation.method(#getScrollPosition, []),
-              returnValue: Future<_i3.Offset>.value(_FakeOffset_2()))
-          as _i5.Future<_i3.Offset>);
-  @override
-  _i5.Future<void> setWebViewClient(_i2.WebViewClient? webViewClient) =>
-      (super.noSuchMethod(Invocation.method(#setWebViewClient, [webViewClient]),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
-  @override
-  _i5.Future<void> addJavaScriptChannel(
-          _i2.JavaScriptChannel? javaScriptChannel) =>
-      (super.noSuchMethod(
-          Invocation.method(#addJavaScriptChannel, [javaScriptChannel]),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
-  @override
-  _i5.Future<void> removeJavaScriptChannel(
-          _i2.JavaScriptChannel? javaScriptChannel) =>
-      (super.noSuchMethod(
-          Invocation.method(#removeJavaScriptChannel, [javaScriptChannel]),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
-  @override
-  _i5.Future<void> setDownloadListener(_i2.DownloadListener? listener) =>
-      (super.noSuchMethod(Invocation.method(#setDownloadListener, [listener]),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
-  @override
-  _i5.Future<void> setWebChromeClient(_i2.WebChromeClient? client) =>
-      (super.noSuchMethod(Invocation.method(#setWebChromeClient, [client]),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
-  @override
-  _i5.Future<void> setBackgroundColor(_i3.Color? color) =>
-      (super.noSuchMethod(Invocation.method(#setBackgroundColor, [color]),
-          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);
-}
-
-/// A class which mocks [WebResourceRequest].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockWebResourceRequest extends _i1.Mock
-    implements _i2.WebResourceRequest {
-  MockWebResourceRequest() {
-    _i1.throwOnMissingStub(this);
-  }
-
-  @override
-  String get url =>
-      (super.noSuchMethod(Invocation.getter(#url), returnValue: '') as String);
-  @override
-  bool get isForMainFrame => (super
-          .noSuchMethod(Invocation.getter(#isForMainFrame), returnValue: false)
-      as bool);
-  @override
-  bool get hasGesture =>
-      (super.noSuchMethod(Invocation.getter(#hasGesture), returnValue: false)
-          as bool);
-  @override
-  String get method =>
-      (super.noSuchMethod(Invocation.getter(#method), returnValue: '')
-          as String);
-  @override
-  Map<String, String> get requestHeaders =>
-      (super.noSuchMethod(Invocation.getter(#requestHeaders),
-          returnValue: <String, String>{}) as Map<String, String>);
-}
-
-/// A class which mocks [WebViewAndroidDownloadListener].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockWebViewAndroidDownloadListener extends _i1.Mock
-    implements _i7.WebViewAndroidDownloadListener {
-  MockWebViewAndroidDownloadListener() {
-    _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);
-  @override
-  _i2.DownloadListener copy() =>
-      (super.noSuchMethod(Invocation.method(#copy, []),
-          returnValue: _FakeDownloadListener_4()) as _i2.DownloadListener);
-}
-
-/// A class which mocks [WebViewAndroidJavaScriptChannel].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockWebViewAndroidJavaScriptChannel extends _i1.Mock
-    implements _i7.WebViewAndroidJavaScriptChannel {
-  MockWebViewAndroidJavaScriptChannel() {
-    _i1.throwOnMissingStub(this);
-  }
-
-  @override
-  _i4.JavascriptChannelRegistry get javascriptChannelRegistry =>
-      (super.noSuchMethod(Invocation.getter(#javascriptChannelRegistry),
-              returnValue: _FakeJavascriptChannelRegistry_5())
-          as _i4.JavascriptChannelRegistry);
-  @override
-  String get channelName =>
-      (super.noSuchMethod(Invocation.getter(#channelName), returnValue: '')
-          as String);
-  @override
-  void postMessage(String? message) =>
-      super.noSuchMethod(Invocation.method(#postMessage, [message]),
-          returnValueForMissingStub: null);
-  @override
-  _i2.JavaScriptChannel copy() =>
-      (super.noSuchMethod(Invocation.method(#copy, []),
-          returnValue: _FakeJavaScriptChannel_6()) as _i2.JavaScriptChannel);
-}
-
-/// A class which mocks [WebViewAndroidWebChromeClient].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockWebViewAndroidWebChromeClient extends _i1.Mock
-    implements _i7.WebViewAndroidWebChromeClient {
-  MockWebViewAndroidWebChromeClient() {
-    _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].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockWebViewAndroidWebViewClient extends _i1.Mock
-    implements _i7.WebViewAndroidWebViewClient {
-  MockWebViewAndroidWebViewClient() {
-    _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);
-}
-
-/// A class which mocks [JavascriptChannelRegistry].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockJavascriptChannelRegistry extends _i1.Mock
-    implements _i4.JavascriptChannelRegistry {
-  MockJavascriptChannelRegistry() {
-    _i1.throwOnMissingStub(this);
-  }
-
-  @override
-  Map<String, _i4.JavascriptChannel> get channels =>
-      (super.noSuchMethod(Invocation.getter(#channels),
-              returnValue: <String, _i4.JavascriptChannel>{})
-          as Map<String, _i4.JavascriptChannel>);
-  @override
-  void onJavascriptChannelMessage(String? channel, String? message) =>
-      super.noSuchMethod(
-          Invocation.method(#onJavascriptChannelMessage, [channel, message]),
-          returnValueForMissingStub: null);
-  @override
-  void updateJavascriptChannelsFromSet(Set<_i4.JavascriptChannel>? channels) =>
-      super.noSuchMethod(
-          Invocation.method(#updateJavascriptChannelsFromSet, [channels]),
-          returnValueForMissingStub: null);
-}
-
-/// A class which mocks [WebViewPlatformCallbacksHandler].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockWebViewPlatformCallbacksHandler extends _i1.Mock
-    implements _i4.WebViewPlatformCallbacksHandler {
-  MockWebViewPlatformCallbacksHandler() {
-    _i1.throwOnMissingStub(this);
-  }
-
-  @override
-  _i5.FutureOr<bool> onNavigationRequest({String? url, bool? isForMainFrame}) =>
-      (super.noSuchMethod(
-          Invocation.method(#onNavigationRequest, [],
-              {#url: url, #isForMainFrame: isForMainFrame}),
-          returnValue: Future<bool>.value(false)) as _i5.FutureOr<bool>);
-  @override
-  void onPageStarted(String? url) =>
-      super.noSuchMethod(Invocation.method(#onPageStarted, [url]),
-          returnValueForMissingStub: null);
-  @override
-  void onPageFinished(String? url) =>
-      super.noSuchMethod(Invocation.method(#onPageFinished, [url]),
-          returnValueForMissingStub: null);
-  @override
-  void onProgress(int? progress) =>
-      super.noSuchMethod(Invocation.method(#onProgress, [progress]),
-          returnValueForMissingStub: null);
-  @override
-  void onWebResourceError(_i4.WebResourceError? error) =>
-      super.noSuchMethod(Invocation.method(#onWebResourceError, [error]),
-          returnValueForMissingStub: null);
-}
-
-/// A class which mocks [WebViewProxy].
-///
-/// See the documentation for Mockito's code generation for more information.
-class MockWebViewProxy extends _i1.Mock implements _i7.WebViewProxy {
-  MockWebViewProxy() {
-    _i1.throwOnMissingStub(this);
-  }
-
-  @override
-  _i2.WebView createWebView({bool? useHybridComposition}) =>
-      (super.noSuchMethod(
-          Invocation.method(#createWebView, [],
-              {#useHybridComposition: useHybridComposition}),
-          returnValue: _FakeWebView_3()) as _i2.WebView);
-  @override
-  _i5.Future<void> setWebContentsDebuggingEnabled(bool? enabled) =>
-      (super.noSuchMethod(
-          Invocation.method(#setWebContentsDebuggingEnabled, [enabled]),
-          returnValue: Future<void>.value(),
-          returnValueForMissingStub: Future<void>.value()) as _i5.Future<void>);
-}
diff --git a/script/configs/exclude_all_plugins_app.yaml b/script/configs/exclude_all_plugins_app.yaml
index 62b20d9..bf00b6c 100644
--- a/script/configs/exclude_all_plugins_app.yaml
+++ b/script/configs/exclude_all_plugins_app.yaml
@@ -8,4 +8,8 @@
 
 # This is a permament entry, as it should never be a direct app dependency.
 - plugin_platform_interface
+# Packages below are temporarily added to push and release a new webview
+# interface. Remove packages with release of `webview_flutter` 4.0.0. See
+# https://github.com/flutter/flutter/issues/94051.
 - webview_flutter_platform_interface
+- webview_flutter_android