Skip to content
This repository was 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
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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
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,13 @@ public void setWebChromeClient(Long instanceId, Long clientInstanceId) {
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;
}
}
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