Skip to content
This repository has been archived by the owner on Feb 22, 2023. It is now read-only.

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

Merged
merged 11 commits into from
Nov 30, 2021
Merged
4 changes: 4 additions & 0 deletions packages/webview_flutter/webview_flutter_android/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 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.

## 2.3.1

* Adds explanation on how to generate the pigeon communication layer and mockito mock objects.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,16 @@ public interface WebViewHostApi {

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);
Expand Down Expand Up @@ -274,6 +284,96 @@ static void setup(BinaryMessenger binaryMessenger, WebViewHostApi api) {
channel.setMessageHandler(null);
}
}
{
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<>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,30 @@ public void dispose(Long instanceId) {
}
}

@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);
Expand Down Expand Up @@ -477,4 +501,12 @@ public void setWebChromeClient(Long instanceId, Long clientInstanceId) {
final WebView webView = (WebView) instanceManager.getInstance(instanceId);
webView.setWebChromeClient((WebChromeClient) instanceManager.getInstance(clientInstanceId));
}

private static String parseNullStringIdentifier(String value) {
mvanbeusekom marked this conversation as resolved.
Show resolved Hide resolved
if (value.equals(nullStringIdentifier)) {
return null;
}

return value;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,52 @@ public void releaseInputAwareWebViewDependents() {
verify(mockJavaScriptChannel2).release();
}

@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<>());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -38,6 +40,25 @@ The navigation delegate is set to block navigation to the youtube website.
</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);

Expand Down Expand Up @@ -135,6 +156,8 @@ enum _MenuOptions {
listCache,
clearCache,
navigationDelegate,
loadLocalFile,
loadHtmlString,
}

class _SampleMenu extends StatelessWidget {
Expand Down Expand Up @@ -172,6 +195,12 @@ class _SampleMenu extends StatelessWidget {
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>>[
Expand Down Expand Up @@ -204,6 +233,14 @@ class _SampleMenu extends StatelessWidget {
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'),
),
],
);
},
Expand Down Expand Up @@ -281,6 +318,18 @@ class _SampleMenu extends StatelessWidget {
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();
Expand All @@ -294,6 +343,16 @@ class _SampleMenu extends StatelessWidget {
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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,28 @@ class WebViewController {

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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ environment:
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
Expand Down
Loading