[webview_flutter] Android implementation of `loadFile` and `loadHtmlString` methods (#4544)

diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md
index 209406c..479364f 100644
--- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md
+++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md
@@ -1,5 +1,6 @@
-## NEXT
+## 2.4.0
 
+* Adds support for Android's `WebView.loadData` and `WebView.loadDataWithBaseUrl` methods and implements the `loadFile` and `loadHtmlString` methods from the platform interface.
 * Updates to webview_flutter_platform_interface version 1.5.2.
 
 ## 2.3.1
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 a342748..beb2d71 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
@@ -173,6 +173,16 @@
 
     void dispose(Long instanceId);
 
+    void loadData(Long instanceId, String data, String mimeType, String encoding);
+
+    void loadDataWithBaseUrl(
+        Long instanceId,
+        String baseUrl,
+        String data,
+        String mimeType,
+        String encoding,
+        String historyUrl);
+
     void loadUrl(Long instanceId, String url, Map<String, String> headers);
 
     String getUrl(Long instanceId);
@@ -277,6 +287,96 @@
       {
         BasicMessageChannel<Object> channel =
             new BasicMessageChannel<>(
+                binaryMessenger, "dev.flutter.pigeon.WebViewHostApi.loadData", 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.");
+                  }
+                  String dataArg = (String) args.get(1);
+                  if (dataArg == null) {
+                    throw new NullPointerException("dataArg unexpectedly null.");
+                  }
+                  String mimeTypeArg = (String) args.get(2);
+                  if (mimeTypeArg == null) {
+                    throw new NullPointerException("mimeTypeArg unexpectedly null.");
+                  }
+                  String encodingArg = (String) args.get(3);
+                  if (encodingArg == null) {
+                    throw new NullPointerException("encodingArg unexpectedly null.");
+                  }
+                  api.loadData(instanceIdArg.longValue(), dataArg, mimeTypeArg, encodingArg);
+                  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.loadDataWithBaseUrl",
+                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.");
+                  }
+                  String baseUrlArg = (String) args.get(1);
+                  if (baseUrlArg == null) {
+                    throw new NullPointerException("baseUrlArg unexpectedly null.");
+                  }
+                  String dataArg = (String) args.get(2);
+                  if (dataArg == null) {
+                    throw new NullPointerException("dataArg unexpectedly null.");
+                  }
+                  String mimeTypeArg = (String) args.get(3);
+                  if (mimeTypeArg == null) {
+                    throw new NullPointerException("mimeTypeArg unexpectedly null.");
+                  }
+                  String encodingArg = (String) args.get(4);
+                  if (encodingArg == null) {
+                    throw new NullPointerException("encodingArg unexpectedly null.");
+                  }
+                  String historyUrlArg = (String) args.get(5);
+                  if (historyUrlArg == null) {
+                    throw new NullPointerException("historyUrlArg unexpectedly null.");
+                  }
+                  api.loadDataWithBaseUrl(
+                      instanceIdArg.longValue(),
+                      baseUrlArg,
+                      dataArg,
+                      mimeTypeArg,
+                      encodingArg,
+                      historyUrlArg);
+                  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.loadUrl", getCodec());
         if (api != null) {
           channel.setMessageHandler(
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 86bef0f..b557e98 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
@@ -353,6 +353,30 @@
   }
 
   @Override
+  public void loadData(Long instanceId, String data, String mimeType, String encoding) {
+    final WebView webView = (WebView) instanceManager.getInstance(instanceId);
+    webView.loadData(
+        data, parseNullStringIdentifier(mimeType), parseNullStringIdentifier(encoding));
+  }
+
+  @Override
+  public void loadDataWithBaseUrl(
+      Long instanceId,
+      String baseUrl,
+      String data,
+      String mimeType,
+      String encoding,
+      String historyUrl) {
+    final WebView webView = (WebView) instanceManager.getInstance(instanceId);
+    webView.loadDataWithBaseURL(
+        parseNullStringIdentifier(baseUrl),
+        data,
+        parseNullStringIdentifier(mimeType),
+        parseNullStringIdentifier(encoding),
+        parseNullStringIdentifier(historyUrl));
+  }
+
+  @Override
   public void loadUrl(Long instanceId, String url, Map<String, String> headers) {
     final WebView webView = (WebView) instanceManager.getInstance(instanceId);
     webView.loadUrl(url, headers);
@@ -477,4 +501,13 @@
     final WebView webView = (WebView) instanceManager.getInstance(instanceId);
     webView.setWebChromeClient((WebChromeClient) instanceManager.getInstance(clientInstanceId));
   }
+
+  @Nullable
+  private static String parseNullStringIdentifier(String value) {
+    if (value.equals(nullStringIdentifier)) {
+      return null;
+    }
+
+    return value;
+  }
 }
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 e506188..dc58b9b 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
@@ -157,6 +157,52 @@
   }
 
   @Test
+  public void loadData() {
+    testHostApiImpl.loadData(
+        0L, "VGhpcyBkYXRhIGlzIGJhc2U2NCBlbmNvZGVkLg==", "text/plain", "base64");
+    verify(mockWebView)
+        .loadData("VGhpcyBkYXRhIGlzIGJhc2U2NCBlbmNvZGVkLg==", "text/plain", "base64");
+  }
+
+  @Test
+  public void loadDataWithNullValues() {
+    testHostApiImpl.loadData(
+        0L, "VGhpcyBkYXRhIGlzIGJhc2U2NCBlbmNvZGVkLg==", "<null-value>", "<null-value>");
+    verify(mockWebView).loadData("VGhpcyBkYXRhIGlzIGJhc2U2NCBlbmNvZGVkLg==", null, null);
+  }
+
+  @Test
+  public void loadDataWithBaseUrl() {
+    testHostApiImpl.loadDataWithBaseUrl(
+        0L,
+        "https://flutter.dev",
+        "VGhpcyBkYXRhIGlzIGJhc2U2NCBlbmNvZGVkLg==",
+        "text/plain",
+        "base64",
+        "about:blank");
+    verify(mockWebView)
+        .loadDataWithBaseURL(
+            "https://flutter.dev",
+            "VGhpcyBkYXRhIGlzIGJhc2U2NCBlbmNvZGVkLg==",
+            "text/plain",
+            "base64",
+            "about:blank");
+  }
+
+  @Test
+  public void loadDataWithBaseUrlAndNullValues() {
+    testHostApiImpl.loadDataWithBaseUrl(
+        0L,
+        "<null-value>",
+        "VGhpcyBkYXRhIGlzIGJhc2U2NCBlbmNvZGVkLg==",
+        "<null-value>",
+        "<null-value>",
+        "<null-value>");
+    verify(mockWebView)
+        .loadDataWithBaseURL(null, "VGhpcyBkYXRhIGlzIGJhc2U2NCBlbmNvZGVkLg==", null, null, null);
+  }
+
+  @Test
   public void loadUrl() {
     testHostApiImpl.loadUrl(0L, "https://www.google.com", new HashMap<>());
     verify(mockWebView).loadUrl("https://www.google.com", new HashMap<>());
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 9041f9c..bc43c16 100644
--- a/packages/webview_flutter/webview_flutter_android/example/lib/main.dart
+++ b/packages/webview_flutter/webview_flutter_android/example/lib/main.dart
@@ -6,8 +6,10 @@
 
 import 'dart:async';
 import 'dart:convert';
+import 'dart:io';
 import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
+import 'package:path_provider/path_provider.dart';
 import 'package:webview_flutter_android/webview_surface_android.dart';
 import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';
 
@@ -38,6 +40,25 @@
 </html>
 ''';
 
+const String kExamplePage = '''
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<title>Load file or HTML string example</title>
+</head>
+<body>
+
+<h1>Local demo page</h1>
+<p>
+  This is an example page used to demonstrate how to load a local file or HTML 
+  string using the <a href="https://pub.dev/packages/webview_flutter">Flutter 
+  webview</a> plugin.
+</p>
+
+</body>
+</html>
+''';
+
 class _WebViewExample extends StatefulWidget {
   const _WebViewExample({Key? key}) : super(key: key);
 
@@ -135,6 +156,8 @@
   listCache,
   clearCache,
   navigationDelegate,
+  loadLocalFile,
+  loadHtmlString,
 }
 
 class _SampleMenu extends StatelessWidget {
@@ -172,6 +195,12 @@
               case _MenuOptions.navigationDelegate:
                 _onNavigationDelegateExample(controller.data!, context);
                 break;
+              case _MenuOptions.loadLocalFile:
+                _onLoadLocalFileExample(controller.data!, context);
+                break;
+              case _MenuOptions.loadHtmlString:
+                _onLoadHtmlStringExample(controller.data!, context);
+                break;
             }
           },
           itemBuilder: (BuildContext context) => <PopupMenuItem<_MenuOptions>>[
@@ -204,6 +233,14 @@
               value: _MenuOptions.navigationDelegate,
               child: Text('Navigation Delegate example'),
             ),
+            const PopupMenuItem<_MenuOptions>(
+              value: _MenuOptions.loadHtmlString,
+              child: Text('Load HTML string'),
+            ),
+            const PopupMenuItem<_MenuOptions>(
+              value: _MenuOptions.loadLocalFile,
+              child: Text('Load local file'),
+            ),
           ],
         );
       },
@@ -281,6 +318,18 @@
     await controller.loadUrl('data:text/html;base64,$contentBase64');
   }
 
+  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);
+  }
+
   Widget _getCookieList(String cookies) {
     if (cookies == null || cookies == '""') {
       return Container();
@@ -294,6 +343,16 @@
       children: cookieWidgets.toList(),
     );
   }
+
+  static Future<String> _prepareLocalFile() async {
+    final String tmpDir = (await getTemporaryDirectory()).path;
+    final File indexFile = File('$tmpDir/www/index.html');
+
+    await Directory('$tmpDir/www').create(recursive: true);
+    await indexFile.writeAsString(kExamplePage);
+
+    return indexFile.path;
+  }
 }
 
 class _NavigationControls extends StatelessWidget {
diff --git a/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart b/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart
index 77d92c6..654abbd 100644
--- a/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart
+++ b/packages/webview_flutter/webview_flutter_android/example/lib/web_view.dart
@@ -363,6 +363,28 @@
 
   WebView _widget;
 
+  /// Loads the file located on the specified [absoluteFilePath].
+  ///
+  /// The [absoluteFilePath] parameter should contain the absolute path to the
+  /// file as it is stored on the device. For example:
+  /// `/Users/username/Documents/www/index.html`.
+  ///
+  /// Throws an ArgumentError if the [absoluteFilePath] does not exist.
+  Future<void> loadFile(String absoluteFilePath) {
+    return _webViewPlatformController.loadFile(absoluteFilePath);
+  }
+
+  /// Loads the supplied HTML string.
+  ///
+  /// The [baseUrl] parameter is used when resolving relative URLs within the
+  /// HTML string.
+  Future<void> loadHtmlString(String html, {String? baseUrl}) {
+    return _webViewPlatformController.loadHtmlString(
+      html,
+      baseUrl: baseUrl,
+    );
+  }
+
   /// Loads the specified URL.
   ///
   /// If `headers` is not null and the URL is an HTTP URL, the key value paris in `headers` will
diff --git a/packages/webview_flutter/webview_flutter_android/example/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/example/pubspec.yaml
index 2faa743..59579df 100644
--- a/packages/webview_flutter/webview_flutter_android/example/pubspec.yaml
+++ b/packages/webview_flutter/webview_flutter_android/example/pubspec.yaml
@@ -8,6 +8,9 @@
 dependencies:
   flutter:
     sdk: flutter
+  
+  path_provider: ^2.0.6
+  
   webview_flutter_android:
     # When depending on this package from a real application you should use:
     #   webview_flutter: ^x.y.z
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 1d0dc25..928cdb3 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
@@ -66,6 +66,95 @@
     return api.setWebContentsDebuggingEnabled(enabled);
   }
 
+  /// Loads the given data into this WebView using a 'data' scheme URL.
+  ///
+  /// Note that JavaScript's same origin policy means that script running in a
+  /// page loaded using this method will be unable to access content loaded
+  /// using any scheme other than 'data', including 'http(s)'. To avoid this
+  /// restriction, use [loadDataWithBaseURL()] with an appropriate base URL.
+  ///
+  /// The [encoding] parameter specifies whether the data is base64 or URL
+  /// encoded. If the data is base64 encoded, the value of the encoding
+  /// parameter must be `'base64'`. HTML can be encoded with
+  /// `base64.encode(bytes)` like so:
+  /// ```dart
+  /// import 'dart:convert';
+  ///
+  /// final unencodedHtml = '''
+  ///   <html><body>'%28' is the code for '('</body></html>
+  /// ''';
+  /// final encodedHtml = base64.encode(utf8.encode(unencodedHtml));
+  /// print(encodedHtml);
+  /// ```
+  ///
+  /// The [mimeType] parameter specifies the format of the data. If WebView
+  /// can't handle the specified MIME type, it will download the data. If
+  /// `null`, defaults to 'text/html'.
+  Future<void> loadData({
+    required String data,
+    String? mimeType,
+    String? encoding,
+  }) {
+    return api.loadDataFromInstance(
+      this,
+      data,
+      mimeType ?? _nullStringIdentifier,
+      encoding ?? _nullStringIdentifier,
+    );
+  }
+
+  /// Loads the given data into this WebView.
+  ///
+  /// The [baseUrl] is used as base URL for the content. It is used  both to
+  /// resolve relative URLs and when applying JavaScript's same origin policy.
+  ///
+  /// The [historyUrl] is used for the history entry.
+  ///
+  /// The [mimeType] parameter specifies the format of the data. If WebView
+  /// can't handle the specified MIME type, it will download the data. If
+  /// `null`, defaults to 'text/html'.
+  ///
+  /// Note that content specified in this way can access local device files (via
+  /// 'file' scheme URLs) only if baseUrl specifies a scheme other than 'http',
+  /// 'https', 'ftp', 'ftps', 'about' or 'javascript'.
+  ///
+  /// If the base URL uses the data scheme, this method is equivalent to calling
+  /// [loadData] and the [historyUrl] is ignored, and the data will be treated
+  /// as part of a data: URL, including the requirement that the content be
+  /// URL-encoded or base64 encoded. If the base URL uses any other scheme, then
+  /// the data will be loaded into the WebView as a plain string (i.e. not part
+  /// of a data URL) and any URL-encoded entities in the string will not be
+  /// decoded.
+  ///
+  /// Note that the [baseUrl] is sent in the 'Referer' HTTP header when
+  /// requesting subresources (images, etc.) of the page loaded using this
+  /// method.
+  ///
+  /// If a valid HTTP or HTTPS base URL is not specified in [baseUrl], then
+  /// content loaded using this method will have a `window.origin` value of
+  /// `"null"`. This must not be considered to be a trusted origin by the
+  /// application or by any JavaScript code running inside the WebView (for
+  /// example, event sources in DOM event handlers or web messages), because
+  /// malicious content can also create frames with a null origin. If you need
+  /// to identify the main frame's origin in a trustworthy way, you should use a
+  /// valid HTTP or HTTPS base URL to set the origin.
+  Future<void> loadDataWithBaseUrl({
+    String? baseUrl,
+    required String data,
+    String? mimeType,
+    String? encoding,
+    String? historyUrl,
+  }) {
+    return api.loadDataWithBaseUrlFromInstance(
+      this,
+      baseUrl ?? _nullStringIdentifier,
+      data,
+      mimeType ?? _nullStringIdentifier,
+      encoding ?? _nullStringIdentifier,
+      historyUrl ?? _nullStringIdentifier,
+    );
+  }
+
   /// Loads the given URL with additional HTTP headers, specified as a map from name to value.
   ///
   /// Note that if this map contains any of the headers that are set by default
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 7fb95f1..9bf2de6 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
@@ -129,6 +129,70 @@
     }
   }
 
+  Future<void> loadData(int arg_instanceId, String arg_data,
+      String arg_mimeType, String arg_encoding) async {
+    final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+        'dev.flutter.pigeon.WebViewHostApi.loadData', codec,
+        binaryMessenger: _binaryMessenger);
+    final Map<Object?, Object?>? replyMap = await channel.send(
+            <Object>[arg_instanceId, arg_data, arg_mimeType, arg_encoding])
+        as Map<Object?, Object?>?;
+    if (replyMap == null) {
+      throw PlatformException(
+        code: 'channel-error',
+        message: 'Unable to establish connection on channel.',
+        details: null,
+      );
+    } 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> loadDataWithBaseUrl(
+      int arg_instanceId,
+      String arg_baseUrl,
+      String arg_data,
+      String arg_mimeType,
+      String arg_encoding,
+      String arg_historyUrl) async {
+    final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+        'dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl', codec,
+        binaryMessenger: _binaryMessenger);
+    final Map<Object?, Object?>? replyMap = await channel.send(<Object>[
+      arg_instanceId,
+      arg_baseUrl,
+      arg_data,
+      arg_mimeType,
+      arg_encoding,
+      arg_historyUrl
+    ]) as Map<Object?, Object?>?;
+    if (replyMap == null) {
+      throw PlatformException(
+        code: 'channel-error',
+        message: 'Unable to establish connection on channel.',
+        details: null,
+      );
+    } 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> loadUrl(int arg_instanceId, String arg_url,
       Map<String?, String?> arg_headers) 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 93391a5..ece05b5 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
@@ -109,6 +109,40 @@
     instanceManager.removeInstance(instance);
   }
 
+  /// Helper method to convert the instances ids to objects.
+  Future<void> loadDataFromInstance(
+    WebView instance,
+    String data,
+    String mimeType,
+    String encoding,
+  ) {
+    return loadData(
+      instanceManager.getInstanceId(instance)!,
+      data,
+      mimeType,
+      encoding,
+    );
+  }
+
+  /// Helper method to convert instances ids to objects.
+  Future<void> loadDataWithBaseUrlFromInstance(
+    WebView instance,
+    String baseUrl,
+    String data,
+    String mimeType,
+    String encoding,
+    String historyUrl,
+  ) {
+    return loadDataWithBaseUrl(
+      instanceManager.getInstanceId(instance)!,
+      baseUrl,
+      data,
+      mimeType,
+      encoding,
+      historyUrl,
+    );
+  }
+
   /// Helper method to convert instances ids to objects.
   Future<void> loadUrlFromInstance(
     WebView instance,
diff --git a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart
index d63d641..c264bc1 100644
--- a/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart
+++ b/packages/webview_flutter/webview_flutter_android/lib/webview_android_widget.dart
@@ -149,6 +149,24 @@
   WebViewAndroidWebViewClient get webViewClient => _webViewClient;
 
   @override
+  Future<void> loadHtmlString(String html, {String? baseUrl}) {
+    return webView.loadDataWithBaseUrl(
+      baseUrl: baseUrl,
+      data: html,
+      mimeType: 'text/html',
+    );
+  }
+
+  @override
+  Future<void> loadFile(String absoluteFilePath) {
+    final String url = absoluteFilePath.startsWith('file://')
+        ? absoluteFilePath
+        : 'file://$absoluteFilePath';
+
+    return webView.loadUrl(url, <String, String>{});
+  }
+
+  @override
   Future<void> loadUrl(
     String url,
     Map<String, String>? headers,
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 47bc420..9632163 100644
--- a/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart
+++ b/packages/webview_flutter/webview_flutter_android/pigeons/android_webview.dart
@@ -24,6 +24,22 @@
 
   void dispose(int instanceId);
 
+  void loadData(
+    int instanceId,
+    String data,
+    String mimeType,
+    String encoding,
+  );
+
+  void loadDataWithBaseUrl(
+    int instanceId,
+    String baseUrl,
+    String data,
+    String mimeType,
+    String encoding,
+    String historyUrl,
+  );
+
   void loadUrl(
     int instanceId,
     String url,
diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml
index a9fa97d..8889469 100644
--- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml
+++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml
@@ -2,7 +2,7 @@
 description: A Flutter plugin that provides a WebView widget on Android.
 repository: https://github.com/flutter/plugins/tree/master/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.3.1
+version: 2.4.0
 
 environment:
   sdk: ">=2.14.0 <3.0.0"
diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart
index 07bbebb..45988a0 100644
--- a/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart
+++ b/packages/webview_flutter/webview_flutter_android/test/android_webview.pigeon.dart
@@ -4,7 +4,7 @@
 
 // Autogenerated from Pigeon (v1.0.9), 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, avoid_relative_lib_imports
+// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, avoid_relative_lib_imports, unnecessary_parenthesis
 // @dart = 2.12
 import 'dart:async';
 import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List;
@@ -23,6 +23,9 @@
 
   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,
+      String mimeType, String encoding, String historyUrl);
   void loadUrl(int instanceId, String url, Map<String?, String?> headers);
   String getUrl(int instanceId);
   bool canGoBack(int instanceId);
@@ -56,10 +59,10 @@
           assert(message != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.create was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = args[0] as int?;
+          final int? arg_instanceId = (args[0] as int?);
           assert(arg_instanceId != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.create was null, expected non-null int.');
-          final bool? arg_useHybridComposition = args[1] as bool?;
+          final bool? arg_useHybridComposition = (args[1] as bool?);
           assert(arg_useHybridComposition != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.create was null, expected non-null bool.');
           api.create(arg_instanceId!, arg_useHybridComposition!);
@@ -78,7 +81,7 @@
           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?;
+          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!);
@@ -88,6 +91,70 @@
     }
     {
       final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+          'dev.flutter.pigeon.WebViewHostApi.loadData', codec,
+          binaryMessenger: binaryMessenger);
+      if (api == null) {
+        channel.setMockMessageHandler(null);
+      } else {
+        channel.setMockMessageHandler((Object? message) async {
+          assert(message != null,
+              'Argument for dev.flutter.pigeon.WebViewHostApi.loadData 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.loadData was null, expected non-null int.');
+          final String? arg_data = (args[1] as String?);
+          assert(arg_data != null,
+              'Argument for dev.flutter.pigeon.WebViewHostApi.loadData was null, expected non-null String.');
+          final String? arg_mimeType = (args[2] as String?);
+          assert(arg_mimeType != null,
+              'Argument for dev.flutter.pigeon.WebViewHostApi.loadData was null, expected non-null String.');
+          final String? arg_encoding = (args[3] as String?);
+          assert(arg_encoding != null,
+              'Argument for dev.flutter.pigeon.WebViewHostApi.loadData was null, expected non-null String.');
+          api.loadData(
+              arg_instanceId!, arg_data!, arg_mimeType!, arg_encoding!);
+          return <Object?, Object?>{};
+        });
+      }
+    }
+    {
+      final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+          'dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl', codec,
+          binaryMessenger: binaryMessenger);
+      if (api == null) {
+        channel.setMockMessageHandler(null);
+      } else {
+        channel.setMockMessageHandler((Object? message) async {
+          assert(message != null,
+              'Argument for dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl 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.loadDataWithBaseUrl was null, expected non-null int.');
+          final String? arg_baseUrl = (args[1] as String?);
+          assert(arg_baseUrl != null,
+              'Argument for dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl was null, expected non-null String.');
+          final String? arg_data = (args[2] as String?);
+          assert(arg_data != null,
+              'Argument for dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl was null, expected non-null String.');
+          final String? arg_mimeType = (args[3] as String?);
+          assert(arg_mimeType != null,
+              'Argument for dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl was null, expected non-null String.');
+          final String? arg_encoding = (args[4] as String?);
+          assert(arg_encoding != null,
+              'Argument for dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl was null, expected non-null String.');
+          final String? arg_historyUrl = (args[5] as String?);
+          assert(arg_historyUrl != null,
+              'Argument for dev.flutter.pigeon.WebViewHostApi.loadDataWithBaseUrl was null, expected non-null String.');
+          api.loadDataWithBaseUrl(arg_instanceId!, arg_baseUrl!, arg_data!,
+              arg_mimeType!, arg_encoding!, arg_historyUrl!);
+          return <Object?, Object?>{};
+        });
+      }
+    }
+    {
+      final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
           'dev.flutter.pigeon.WebViewHostApi.loadUrl', codec,
           binaryMessenger: binaryMessenger);
       if (api == null) {
@@ -97,10 +164,10 @@
           assert(message != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.loadUrl was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = args[0] as int?;
+          final int? arg_instanceId = (args[0] as int?);
           assert(arg_instanceId != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.loadUrl was null, expected non-null int.');
-          final String? arg_url = args[1] as String?;
+          final String? arg_url = (args[1] as String?);
           assert(arg_url != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.loadUrl was null, expected non-null String.');
           final Map<String?, String?>? arg_headers =
@@ -123,7 +190,7 @@
           assert(message != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.getUrl was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = args[0] as int?;
+          final int? arg_instanceId = (args[0] as int?);
           assert(arg_instanceId != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.getUrl was null, expected non-null int.');
           final String output = api.getUrl(arg_instanceId!);
@@ -142,7 +209,7 @@
           assert(message != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.canGoBack was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = args[0] as int?;
+          final int? arg_instanceId = (args[0] as int?);
           assert(arg_instanceId != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.canGoBack was null, expected non-null int.');
           final bool output = api.canGoBack(arg_instanceId!);
@@ -161,7 +228,7 @@
           assert(message != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.canGoForward was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = args[0] as int?;
+          final int? arg_instanceId = (args[0] as int?);
           assert(arg_instanceId != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.canGoForward was null, expected non-null int.');
           final bool output = api.canGoForward(arg_instanceId!);
@@ -180,7 +247,7 @@
           assert(message != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.goBack was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = args[0] as int?;
+          final int? arg_instanceId = (args[0] as int?);
           assert(arg_instanceId != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.goBack was null, expected non-null int.');
           api.goBack(arg_instanceId!);
@@ -199,7 +266,7 @@
           assert(message != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.goForward was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = args[0] as int?;
+          final int? arg_instanceId = (args[0] as int?);
           assert(arg_instanceId != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.goForward was null, expected non-null int.');
           api.goForward(arg_instanceId!);
@@ -218,7 +285,7 @@
           assert(message != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.reload was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = args[0] as int?;
+          final int? arg_instanceId = (args[0] as int?);
           assert(arg_instanceId != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.reload was null, expected non-null int.');
           api.reload(arg_instanceId!);
@@ -237,10 +304,10 @@
           assert(message != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.clearCache was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = args[0] as int?;
+          final int? arg_instanceId = (args[0] as int?);
           assert(arg_instanceId != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.clearCache was null, expected non-null int.');
-          final bool? arg_includeDiskFiles = args[1] as bool?;
+          final bool? arg_includeDiskFiles = (args[1] as bool?);
           assert(arg_includeDiskFiles != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.clearCache was null, expected non-null bool.');
           api.clearCache(arg_instanceId!, arg_includeDiskFiles!);
@@ -259,10 +326,10 @@
           assert(message != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.evaluateJavascript was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = args[0] as int?;
+          final int? arg_instanceId = (args[0] as int?);
           assert(arg_instanceId != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.evaluateJavascript was null, expected non-null int.');
-          final String? arg_javascriptString = args[1] as String?;
+          final String? arg_javascriptString = (args[1] as String?);
           assert(arg_javascriptString != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.evaluateJavascript was null, expected non-null String.');
           final String output = await api.evaluateJavascript(
@@ -282,7 +349,7 @@
           assert(message != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.getTitle was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = args[0] as int?;
+          final int? arg_instanceId = (args[0] as int?);
           assert(arg_instanceId != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.getTitle was null, expected non-null int.');
           final String output = api.getTitle(arg_instanceId!);
@@ -301,13 +368,13 @@
           assert(message != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.scrollTo was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = args[0] as int?;
+          final int? arg_instanceId = (args[0] as int?);
           assert(arg_instanceId != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.scrollTo was null, expected non-null int.');
-          final int? arg_x = args[1] as int?;
+          final int? arg_x = (args[1] as int?);
           assert(arg_x != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.scrollTo was null, expected non-null int.');
-          final int? arg_y = args[2] as int?;
+          final int? arg_y = (args[2] as int?);
           assert(arg_y != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.scrollTo was null, expected non-null int.');
           api.scrollTo(arg_instanceId!, arg_x!, arg_y!);
@@ -326,13 +393,13 @@
           assert(message != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.scrollBy was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = args[0] as int?;
+          final int? arg_instanceId = (args[0] as int?);
           assert(arg_instanceId != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.scrollBy was null, expected non-null int.');
-          final int? arg_x = args[1] as int?;
+          final int? arg_x = (args[1] as int?);
           assert(arg_x != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.scrollBy was null, expected non-null int.');
-          final int? arg_y = args[2] as int?;
+          final int? arg_y = (args[2] as int?);
           assert(arg_y != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.scrollBy was null, expected non-null int.');
           api.scrollBy(arg_instanceId!, arg_x!, arg_y!);
@@ -351,7 +418,7 @@
           assert(message != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.getScrollX was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = args[0] as int?;
+          final int? arg_instanceId = (args[0] as int?);
           assert(arg_instanceId != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.getScrollX was null, expected non-null int.');
           final int output = api.getScrollX(arg_instanceId!);
@@ -370,7 +437,7 @@
           assert(message != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.getScrollY was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = args[0] as int?;
+          final int? arg_instanceId = (args[0] as int?);
           assert(arg_instanceId != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.getScrollY was null, expected non-null int.');
           final int output = api.getScrollY(arg_instanceId!);
@@ -390,7 +457,7 @@
           assert(message != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.setWebContentsDebuggingEnabled was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final bool? arg_enabled = args[0] as bool?;
+          final bool? arg_enabled = (args[0] as bool?);
           assert(arg_enabled != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.setWebContentsDebuggingEnabled was null, expected non-null bool.');
           api.setWebContentsDebuggingEnabled(arg_enabled!);
@@ -409,10 +476,10 @@
           assert(message != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.setWebViewClient was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = args[0] as int?;
+          final int? arg_instanceId = (args[0] as int?);
           assert(arg_instanceId != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.setWebViewClient was null, expected non-null int.');
-          final int? arg_webViewClientInstanceId = args[1] as int?;
+          final int? arg_webViewClientInstanceId = (args[1] as int?);
           assert(arg_webViewClientInstanceId != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.setWebViewClient was null, expected non-null int.');
           api.setWebViewClient(arg_instanceId!, arg_webViewClientInstanceId!);
@@ -431,10 +498,10 @@
           assert(message != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.addJavaScriptChannel was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = args[0] as int?;
+          final int? arg_instanceId = (args[0] as int?);
           assert(arg_instanceId != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.addJavaScriptChannel was null, expected non-null int.');
-          final int? arg_javaScriptChannelInstanceId = args[1] as int?;
+          final int? arg_javaScriptChannelInstanceId = (args[1] as int?);
           assert(arg_javaScriptChannelInstanceId != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.addJavaScriptChannel was null, expected non-null int.');
           api.addJavaScriptChannel(
@@ -454,10 +521,10 @@
           assert(message != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.removeJavaScriptChannel was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = args[0] as int?;
+          final int? arg_instanceId = (args[0] as int?);
           assert(arg_instanceId != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.removeJavaScriptChannel was null, expected non-null int.');
-          final int? arg_javaScriptChannelInstanceId = args[1] as int?;
+          final int? arg_javaScriptChannelInstanceId = (args[1] as int?);
           assert(arg_javaScriptChannelInstanceId != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.removeJavaScriptChannel was null, expected non-null int.');
           api.removeJavaScriptChannel(
@@ -477,10 +544,10 @@
           assert(message != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.setDownloadListener was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = args[0] as int?;
+          final int? arg_instanceId = (args[0] as int?);
           assert(arg_instanceId != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.setDownloadListener was null, expected non-null int.');
-          final int? arg_listenerInstanceId = args[1] as int?;
+          final int? arg_listenerInstanceId = (args[1] as int?);
           assert(arg_listenerInstanceId != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.setDownloadListener was null, expected non-null int.');
           api.setDownloadListener(arg_instanceId!, arg_listenerInstanceId!);
@@ -499,10 +566,10 @@
           assert(message != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.setWebChromeClient was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = args[0] as int?;
+          final int? arg_instanceId = (args[0] as int?);
           assert(arg_instanceId != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.setWebChromeClient was null, expected non-null int.');
-          final int? arg_clientInstanceId = args[1] as int?;
+          final int? arg_clientInstanceId = (args[1] as int?);
           assert(arg_clientInstanceId != null,
               'Argument for dev.flutter.pigeon.WebViewHostApi.setWebChromeClient was null, expected non-null int.');
           api.setWebChromeClient(arg_instanceId!, arg_clientInstanceId!);
@@ -546,10 +613,10 @@
           assert(message != null,
               'Argument for dev.flutter.pigeon.WebSettingsHostApi.create was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = args[0] as int?;
+          final int? arg_instanceId = (args[0] as int?);
           assert(arg_instanceId != null,
               'Argument for dev.flutter.pigeon.WebSettingsHostApi.create was null, expected non-null int.');
-          final int? arg_webViewInstanceId = args[1] as int?;
+          final int? arg_webViewInstanceId = (args[1] as int?);
           assert(arg_webViewInstanceId != null,
               'Argument for dev.flutter.pigeon.WebSettingsHostApi.create was null, expected non-null int.');
           api.create(arg_instanceId!, arg_webViewInstanceId!);
@@ -568,7 +635,7 @@
           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?;
+          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!);
@@ -587,10 +654,10 @@
           assert(message != null,
               'Argument for dev.flutter.pigeon.WebSettingsHostApi.setDomStorageEnabled was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = args[0] as int?;
+          final int? arg_instanceId = (args[0] as int?);
           assert(arg_instanceId != null,
               'Argument for dev.flutter.pigeon.WebSettingsHostApi.setDomStorageEnabled was null, expected non-null int.');
-          final bool? arg_flag = args[1] as bool?;
+          final bool? arg_flag = (args[1] as bool?);
           assert(arg_flag != null,
               'Argument for dev.flutter.pigeon.WebSettingsHostApi.setDomStorageEnabled was null, expected non-null bool.');
           api.setDomStorageEnabled(arg_instanceId!, arg_flag!);
@@ -610,10 +677,10 @@
           assert(message != null,
               'Argument for dev.flutter.pigeon.WebSettingsHostApi.setJavaScriptCanOpenWindowsAutomatically was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = args[0] as int?;
+          final int? arg_instanceId = (args[0] as int?);
           assert(arg_instanceId != null,
               'Argument for dev.flutter.pigeon.WebSettingsHostApi.setJavaScriptCanOpenWindowsAutomatically was null, expected non-null int.');
-          final bool? arg_flag = args[1] as bool?;
+          final bool? arg_flag = (args[1] as bool?);
           assert(arg_flag != null,
               'Argument for dev.flutter.pigeon.WebSettingsHostApi.setJavaScriptCanOpenWindowsAutomatically was null, expected non-null bool.');
           api.setJavaScriptCanOpenWindowsAutomatically(
@@ -634,10 +701,10 @@
           assert(message != null,
               'Argument for dev.flutter.pigeon.WebSettingsHostApi.setSupportMultipleWindows was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = args[0] as int?;
+          final int? arg_instanceId = (args[0] as int?);
           assert(arg_instanceId != null,
               'Argument for dev.flutter.pigeon.WebSettingsHostApi.setSupportMultipleWindows was null, expected non-null int.');
-          final bool? arg_support = args[1] as bool?;
+          final bool? arg_support = (args[1] as bool?);
           assert(arg_support != null,
               'Argument for dev.flutter.pigeon.WebSettingsHostApi.setSupportMultipleWindows was null, expected non-null bool.');
           api.setSupportMultipleWindows(arg_instanceId!, arg_support!);
@@ -656,10 +723,10 @@
           assert(message != null,
               'Argument for dev.flutter.pigeon.WebSettingsHostApi.setJavaScriptEnabled was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = args[0] as int?;
+          final int? arg_instanceId = (args[0] as int?);
           assert(arg_instanceId != null,
               'Argument for dev.flutter.pigeon.WebSettingsHostApi.setJavaScriptEnabled was null, expected non-null int.');
-          final bool? arg_flag = args[1] as bool?;
+          final bool? arg_flag = (args[1] as bool?);
           assert(arg_flag != null,
               'Argument for dev.flutter.pigeon.WebSettingsHostApi.setJavaScriptEnabled was null, expected non-null bool.');
           api.setJavaScriptEnabled(arg_instanceId!, arg_flag!);
@@ -678,10 +745,10 @@
           assert(message != null,
               'Argument for dev.flutter.pigeon.WebSettingsHostApi.setUserAgentString was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = args[0] as int?;
+          final int? arg_instanceId = (args[0] as int?);
           assert(arg_instanceId != null,
               'Argument for dev.flutter.pigeon.WebSettingsHostApi.setUserAgentString was null, expected non-null int.');
-          final String? arg_userAgentString = args[1] as String?;
+          final String? arg_userAgentString = (args[1] as String?);
           assert(arg_userAgentString != null,
               'Argument for dev.flutter.pigeon.WebSettingsHostApi.setUserAgentString was null, expected non-null String.');
           api.setUserAgentString(arg_instanceId!, arg_userAgentString!);
@@ -701,10 +768,10 @@
           assert(message != null,
               'Argument for dev.flutter.pigeon.WebSettingsHostApi.setMediaPlaybackRequiresUserGesture was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = args[0] as int?;
+          final int? arg_instanceId = (args[0] as int?);
           assert(arg_instanceId != null,
               'Argument for dev.flutter.pigeon.WebSettingsHostApi.setMediaPlaybackRequiresUserGesture was null, expected non-null int.');
-          final bool? arg_require = args[1] as bool?;
+          final bool? arg_require = (args[1] as bool?);
           assert(arg_require != null,
               'Argument for dev.flutter.pigeon.WebSettingsHostApi.setMediaPlaybackRequiresUserGesture was null, expected non-null bool.');
           api.setMediaPlaybackRequiresUserGesture(
@@ -724,10 +791,10 @@
           assert(message != null,
               'Argument for dev.flutter.pigeon.WebSettingsHostApi.setSupportZoom was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = args[0] as int?;
+          final int? arg_instanceId = (args[0] as int?);
           assert(arg_instanceId != null,
               'Argument for dev.flutter.pigeon.WebSettingsHostApi.setSupportZoom was null, expected non-null int.');
-          final bool? arg_support = args[1] as bool?;
+          final bool? arg_support = (args[1] as bool?);
           assert(arg_support != null,
               'Argument for dev.flutter.pigeon.WebSettingsHostApi.setSupportZoom was null, expected non-null bool.');
           api.setSupportZoom(arg_instanceId!, arg_support!);
@@ -747,10 +814,10 @@
           assert(message != null,
               'Argument for dev.flutter.pigeon.WebSettingsHostApi.setLoadWithOverviewMode was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = args[0] as int?;
+          final int? arg_instanceId = (args[0] as int?);
           assert(arg_instanceId != null,
               'Argument for dev.flutter.pigeon.WebSettingsHostApi.setLoadWithOverviewMode was null, expected non-null int.');
-          final bool? arg_overview = args[1] as bool?;
+          final bool? arg_overview = (args[1] as bool?);
           assert(arg_overview != null,
               'Argument for dev.flutter.pigeon.WebSettingsHostApi.setLoadWithOverviewMode was null, expected non-null bool.');
           api.setLoadWithOverviewMode(arg_instanceId!, arg_overview!);
@@ -769,10 +836,10 @@
           assert(message != null,
               'Argument for dev.flutter.pigeon.WebSettingsHostApi.setUseWideViewPort was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = args[0] as int?;
+          final int? arg_instanceId = (args[0] as int?);
           assert(arg_instanceId != null,
               'Argument for dev.flutter.pigeon.WebSettingsHostApi.setUseWideViewPort was null, expected non-null int.');
-          final bool? arg_use = args[1] as bool?;
+          final bool? arg_use = (args[1] as bool?);
           assert(arg_use != null,
               'Argument for dev.flutter.pigeon.WebSettingsHostApi.setUseWideViewPort was null, expected non-null bool.');
           api.setUseWideViewPort(arg_instanceId!, arg_use!);
@@ -791,10 +858,10 @@
           assert(message != null,
               'Argument for dev.flutter.pigeon.WebSettingsHostApi.setDisplayZoomControls was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = args[0] as int?;
+          final int? arg_instanceId = (args[0] as int?);
           assert(arg_instanceId != null,
               'Argument for dev.flutter.pigeon.WebSettingsHostApi.setDisplayZoomControls was null, expected non-null int.');
-          final bool? arg_enabled = args[1] as bool?;
+          final bool? arg_enabled = (args[1] as bool?);
           assert(arg_enabled != null,
               'Argument for dev.flutter.pigeon.WebSettingsHostApi.setDisplayZoomControls was null, expected non-null bool.');
           api.setDisplayZoomControls(arg_instanceId!, arg_enabled!);
@@ -813,10 +880,10 @@
           assert(message != null,
               'Argument for dev.flutter.pigeon.WebSettingsHostApi.setBuiltInZoomControls was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = args[0] as int?;
+          final int? arg_instanceId = (args[0] as int?);
           assert(arg_instanceId != null,
               'Argument for dev.flutter.pigeon.WebSettingsHostApi.setBuiltInZoomControls was null, expected non-null int.');
-          final bool? arg_enabled = args[1] as bool?;
+          final bool? arg_enabled = (args[1] as bool?);
           assert(arg_enabled != null,
               'Argument for dev.flutter.pigeon.WebSettingsHostApi.setBuiltInZoomControls was null, expected non-null bool.');
           api.setBuiltInZoomControls(arg_instanceId!, arg_enabled!);
@@ -849,10 +916,10 @@
           assert(message != null,
               'Argument for dev.flutter.pigeon.JavaScriptChannelHostApi.create was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = args[0] as int?;
+          final int? arg_instanceId = (args[0] as int?);
           assert(arg_instanceId != null,
               'Argument for dev.flutter.pigeon.JavaScriptChannelHostApi.create was null, expected non-null int.');
-          final String? arg_channelName = args[1] as String?;
+          final String? arg_channelName = (args[1] as String?);
           assert(arg_channelName != null,
               'Argument for dev.flutter.pigeon.JavaScriptChannelHostApi.create was null, expected non-null String.');
           api.create(arg_instanceId!, arg_channelName!);
@@ -884,10 +951,10 @@
           assert(message != null,
               'Argument for dev.flutter.pigeon.WebViewClientHostApi.create was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = args[0] as int?;
+          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?;
+          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!);
@@ -920,7 +987,7 @@
           assert(message != null,
               'Argument for dev.flutter.pigeon.DownloadListenerHostApi.create was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = args[0] as int?;
+          final int? arg_instanceId = (args[0] as int?);
           assert(arg_instanceId != null,
               'Argument for dev.flutter.pigeon.DownloadListenerHostApi.create was null, expected non-null int.');
           api.create(arg_instanceId!);
@@ -952,10 +1019,10 @@
           assert(message != null,
               'Argument for dev.flutter.pigeon.WebChromeClientHostApi.create was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final int? arg_instanceId = args[0] as int?;
+          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?;
+          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!);
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 29c0c3a..0e7d5fc 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
@@ -58,6 +58,61 @@
         verify(mockPlatformHostApi.setWebContentsDebuggingEnabled(true));
       });
 
+      test('loadData', () {
+        webView.loadData(
+          data: 'hello',
+          mimeType: 'text/plain',
+          encoding: 'base64',
+        );
+        verify(mockPlatformHostApi.loadData(
+          webViewInstanceId,
+          'hello',
+          'text/plain',
+          'base64',
+        ));
+      });
+
+      test('loadData with null values', () {
+        webView.loadData(data: 'hello', mimeType: null, encoding: null);
+        verify(mockPlatformHostApi.loadData(
+          webViewInstanceId,
+          'hello',
+          '<null-value>',
+          '<null-value>',
+        ));
+      });
+
+      test('loadDataWithBaseUrl', () {
+        webView.loadDataWithBaseUrl(
+          baseUrl: 'https://base.url',
+          data: 'hello',
+          mimeType: 'text/plain',
+          encoding: 'base64',
+          historyUrl: 'https://history.url',
+        );
+
+        verify(mockPlatformHostApi.loadDataWithBaseUrl(
+          webViewInstanceId,
+          'https://base.url',
+          'hello',
+          'text/plain',
+          'base64',
+          'https://history.url',
+        ));
+      });
+
+      test('loadDataWithBaseUrl with null values', () {
+        webView.loadDataWithBaseUrl(data: 'hello');
+        verify(mockPlatformHostApi.loadDataWithBaseUrl(
+          webViewInstanceId,
+          '<null-value>',
+          'hello',
+          '<null-value>',
+          '<null-value>',
+          '<null-value>',
+        ));
+      });
+
       test('loadUrl', () {
         webView.loadUrl('hello', <String, String>{'a': 'header'});
         verify(mockPlatformHostApi.loadUrl(
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 4378ed7..90cbf2c 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,3 @@
-// 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.
-
 // Mocks generated by Mockito 5.0.16 from annotations
 // in webview_flutter_android/test/android_webview_test.dart.
 // Do not manually edit this file.
@@ -226,6 +222,19 @@
       super.noSuchMethod(Invocation.method(#dispose, [instanceId]),
           returnValueForMissingStub: null);
   @override
+  void loadData(
+          int? instanceId, String? data, String? mimeType, String? encoding) =>
+      super.noSuchMethod(
+          Invocation.method(#loadData, [instanceId, data, mimeType, encoding]),
+          returnValueForMissingStub: null);
+  @override
+  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);
+  @override
   void loadUrl(int? instanceId, String? url, Map<String?, String?>? headers) =>
       super.noSuchMethod(
           Invocation.method(#loadUrl, [instanceId, url, headers]),
@@ -359,6 +368,31 @@
       (super.noSuchMethod(Invocation.getter(#settings),
           returnValue: _FakeWebSettings_0()) as _i2.WebSettings);
   @override
+  _i4.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 _i4.Future<void>);
+  @override
+  _i4.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 _i4.Future<void>);
+  @override
   _i4.Future<void> loadUrl(String? url, Map<String, String>? headers) =>
       (super.noSuchMethod(Invocation.method(#loadUrl, [url, headers]),
           returnValue: Future<void>.value(),
diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart
index d969664..460cb54 100644
--- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart
+++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.dart
@@ -274,6 +274,60 @@
     });
 
     group('$WebViewPlatformController', () {
+      testWidgets('loadFile without "file://" prefix',
+          (WidgetTester tester) async {
+        await buildWidget(tester);
+
+        const String filePath = '/path/to/file.html';
+        await testController.loadFile(filePath);
+
+        verify(mockWebView.loadUrl(
+          'file://$filePath',
+          <String, String>{},
+        ));
+      });
+
+      testWidgets('loadFile with "file://" prefix',
+          (WidgetTester tester) async {
+        await buildWidget(tester);
+
+        await testController.loadFile('file:///path/to/file.html');
+
+        verify(mockWebView.loadUrl(
+          'file:///path/to/file.html',
+          <String, String>{},
+        ));
+      });
+
+      testWidgets('loadHtmlString without base URL',
+          (WidgetTester tester) async {
+        await buildWidget(tester);
+
+        const String htmlString = '<html><body>Test data.</body></html>';
+        await testController.loadHtmlString(htmlString);
+
+        verify(mockWebView.loadDataWithBaseUrl(
+          data: htmlString,
+          mimeType: 'text/html',
+        ));
+      });
+
+      testWidgets('loadHtmlString with base URL', (WidgetTester tester) async {
+        await buildWidget(tester);
+
+        const String htmlString = '<html><body>Test data.</body></html>';
+        await testController.loadHtmlString(
+          htmlString,
+          baseUrl: 'https://flutter.dev',
+        );
+
+        verify(mockWebView.loadDataWithBaseUrl(
+          baseUrl: 'https://flutter.dev',
+          data: htmlString,
+          mimeType: 'text/html',
+        ));
+      });
+
       testWidgets('loadUrl', (WidgetTester tester) async {
         await buildWidget(tester);
 
diff --git a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart
index c9d3324..0582049 100644
--- a/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart
+++ b/packages/webview_flutter/webview_flutter_android/test/webview_android_widget_test.mocks.dart
@@ -1,7 +1,3 @@
-// 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.
-
 // Mocks generated by Mockito 5.0.16 from annotations
 // in webview_flutter_android/test/webview_android_widget_test.dart.
 // Do not manually edit this file.
@@ -116,6 +112,31 @@
       (super.noSuchMethod(Invocation.getter(#settings),
           returnValue: _FakeWebSettings_0()) as _i2.WebSettings);
   @override
+  _i4.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 _i4.Future<void>);
+  @override
+  _i4.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 _i4.Future<void>);
+  @override
   _i4.Future<void> loadUrl(String? url, Map<String, String>? headers) =>
       (super.noSuchMethod(Invocation.method(#loadUrl, [url, headers]),
           returnValue: Future<void>.value(),