updated web support
This commit is contained in:
parent
176d41d328
commit
46fcafcf44
|
@ -119,7 +119,7 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
|
|||
key: webViewKey,
|
||||
// contextMenu: contextMenu,
|
||||
initialUrlRequest:
|
||||
URLRequest(url: Uri.parse("http://flutter.dev/")),
|
||||
URLRequest(url: Uri.parse("https://flutter.dev")),
|
||||
// initialFile: "assets/index.html",
|
||||
initialUserScripts: UnmodifiableListView<UserScript>([]),
|
||||
initialSettings: settings,
|
||||
|
|
|
@ -31,6 +31,9 @@
|
|||
|
||||
<title>flutter_inappwebview_example</title>
|
||||
<link rel="manifest" href="manifest.json">
|
||||
|
||||
<!-- Load flutter_inappwebview web_support js library -->
|
||||
<script src="/packages/flutter_inappwebview/assets/web/web_support.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<!-- This script installs service_worker.js to provide PWA functionality to
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<base href="$FLUTTER_BASE_HREF">
|
||||
|
||||
<meta charset="UTF-8">
|
||||
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
|
||||
<meta name="description" content="Demonstrates how to use the flutter_inappwebview plugin.">
|
||||
|
||||
<!-- iOS meta tags & icons -->
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||
<meta name="apple-mobile-web-app-title" content="flutter_inappwebview_example">
|
||||
<link rel="apple-touch-icon" href="icons/Icon-192.png">
|
||||
|
||||
<!-- Favicon -->
|
||||
<link rel="icon" type="image/png" href="favicon.png"/>
|
||||
|
||||
<title>flutter_inappwebview_example</title>
|
||||
<link rel="manifest" href="manifest.json">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Simple Page 2</h1>
|
||||
<a href="/page.html">Go to page 1</a>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,26 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<base href="$FLUTTER_BASE_HREF">
|
||||
|
||||
<meta charset="UTF-8">
|
||||
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
|
||||
<meta name="description" content="Demonstrates how to use the flutter_inappwebview plugin.">
|
||||
|
||||
<!-- iOS meta tags & icons -->
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||
<meta name="apple-mobile-web-app-title" content="flutter_inappwebview_example">
|
||||
<link rel="apple-touch-icon" href="icons/Icon-192.png">
|
||||
|
||||
<!-- Favicon -->
|
||||
<link rel="icon" type="image/png" href="favicon.png"/>
|
||||
|
||||
<title>flutter_inappwebview_example</title>
|
||||
<link rel="manifest" href="manifest.json">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Simple Page 1</h1>
|
||||
<a href="/page-2.html">Go to page 2</a>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,63 @@
|
|||
window.flutter_inappwebview = {
|
||||
viewId: null,
|
||||
iframeId: null,
|
||||
iframe: null,
|
||||
prepare: function () {
|
||||
var iframe = document.getElementById(window.flutter_inappwebview.iframeId);
|
||||
if (iframe != null) {
|
||||
window.flutter_inappwebview.iframe = iframe;
|
||||
iframe.addEventListener('load', function (event) {
|
||||
var url = iframe.src;
|
||||
try {
|
||||
url = iframe.contentWindow.location.href;
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
window.flutter_inappwebview.nativeCommunication('iframeLoaded', window.flutter_inappwebview.viewId, [url]);
|
||||
});
|
||||
}
|
||||
},
|
||||
reload: function () {
|
||||
var iframe = window.flutter_inappwebview.iframe;
|
||||
if (iframe != null && iframe.contentWindow != null) {
|
||||
try {
|
||||
iframe.contentWindow.location.reload();
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
iframe.contentWindow.location.href = iframe.src;
|
||||
}
|
||||
}
|
||||
},
|
||||
goBack: function () {
|
||||
var iframe = window.flutter_inappwebview.iframe;
|
||||
if (iframe != null) {
|
||||
try {
|
||||
iframe.contentWindow.history.back();
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
},
|
||||
goForward: function () {
|
||||
var iframe = window.flutter_inappwebview.iframe;
|
||||
if (iframe != null) {
|
||||
try {
|
||||
iframe.contentWindow.history.forward();
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
},
|
||||
evaluateJavascript: function (source) {
|
||||
var iframe = window.flutter_inappwebview.iframe;
|
||||
var result = null;
|
||||
if (iframe != null) {
|
||||
try {
|
||||
result = iframe.contentWindow.eval(source);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
|
@ -1385,9 +1385,13 @@ class InAppWebViewController
|
|||
///
|
||||
///**NOTE for Android**: when loading an URL Request using "POST" method, headers are ignored.
|
||||
///
|
||||
///**NOTE for Web**: if method is "GET" and headers are empty, it will change the `src` of the iframe.
|
||||
///For all other cases it will try to create an XMLHttpRequest and load the result inside the iframe.
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView ([Official API - WebView.loadUrl](https://developer.android.com/reference/android/webkit/WebView#loadUrl(java.lang.String))). If method is "POST", [Official API - WebView.postUrl](https://developer.android.com/reference/android/webkit/WebView#postUrl(java.lang.String,%20byte[]))
|
||||
///- iOS ([Official API - WKWebView.load](https://developer.apple.com/documentation/webkit/wkwebview/1414954-load). If [allowingReadAccessTo] is used, [Official API - WKWebView.loadFileURL](https://developer.apple.com/documentation/webkit/wkwebview/1414973-loadfileurl))
|
||||
///- Web
|
||||
Future<void> loadUrl(
|
||||
{required URLRequest urlRequest,
|
||||
@Deprecated('Use allowingReadAccessTo instead')
|
||||
|
@ -1432,7 +1436,9 @@ class InAppWebViewController
|
|||
///
|
||||
///- [mimeType] argument specifies the format of the data. The default value is `"text/html"`.
|
||||
///- [encoding] argument specifies the encoding of the data. The default value is `"utf8"`.
|
||||
///**NOTE**: not used on Web.
|
||||
///- [historyUrl] is an Android-specific argument that represents the URL to use as the history entry. The default value is `about:blank`. If non-null, this must be a valid URL.
|
||||
///**NOTE**: not used on Web.
|
||||
///- [allowingReadAccessTo], used in combination with [baseUrl] (using the `file://` scheme),
|
||||
///it represents the URL from which to read the web content.
|
||||
///This [baseUrl] must be a file-based URL (using the `file://` scheme).
|
||||
|
@ -1443,6 +1449,7 @@ class InAppWebViewController
|
|||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView ([Official API - WebView.loadDataWithBaseURL](https://developer.android.com/reference/android/webkit/WebView#loadDataWithBaseURL(java.lang.String,%20java.lang.String,%20java.lang.String,%20java.lang.String,%20java.lang.String)))
|
||||
///- iOS ([Official API - WKWebView.loadHTMLString](https://developer.apple.com/documentation/webkit/wkwebview/1415004-loadhtmlstring) or [Official API - WKWebView.load](https://developer.apple.com/documentation/webkit/wkwebview/1415011-load))
|
||||
///- Web
|
||||
Future<void> loadData(
|
||||
{required String data,
|
||||
String mimeType = "text/html",
|
||||
|
@ -1511,6 +1518,7 @@ class InAppWebViewController
|
|||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView ([Official API - WebView.loadUrl](https://developer.android.com/reference/android/webkit/WebView#loadUrl(java.lang.String)))
|
||||
///- iOS ([Official API - WKWebView.load](https://developer.apple.com/documentation/webkit/wkwebview/1414954-load))
|
||||
///- Web
|
||||
Future<void> loadFile({required String assetFilePath}) async {
|
||||
assert(assetFilePath.isNotEmpty);
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
|
@ -1520,9 +1528,12 @@ class InAppWebViewController
|
|||
|
||||
///Reloads the WebView.
|
||||
///
|
||||
///**NOTE**: on Web, if `window.location.reload()` is not accessible inside the iframe, it will reload using the iframe `src` attribute.
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView ([Official API - WebView.reload](https://developer.android.com/reference/android/webkit/WebView#reload()))
|
||||
///- iOS ([Official API - WKWebView.reload](https://developer.apple.com/documentation/webkit/wkwebview/1414969-reload))
|
||||
///- Web ([Official API - Location.reload](https://developer.mozilla.org/en-US/docs/Web/API/Location/reload))
|
||||
Future<void> reload() async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
await _channel.invokeMethod('reload', args);
|
||||
|
@ -1533,6 +1544,7 @@ class InAppWebViewController
|
|||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView ([Official API - WebView.goBack](https://developer.android.com/reference/android/webkit/WebView#goBack()))
|
||||
///- iOS ([Official API - WKWebView.goBack](https://developer.apple.com/documentation/webkit/wkwebview/1414952-goback))
|
||||
///- Web ([Official API - History.back](https://developer.mozilla.org/en-US/docs/Web/API/History/back))
|
||||
Future<void> goBack() async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
await _channel.invokeMethod('goBack', args);
|
||||
|
@ -1553,6 +1565,7 @@ class InAppWebViewController
|
|||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView ([Official API - WebView.goForward](https://developer.android.com/reference/android/webkit/WebView#goForward()))
|
||||
///- iOS ([Official API - WKWebView.goForward](https://developer.apple.com/documentation/webkit/wkwebview/1414993-goforward))
|
||||
///- Web ([Official API - History.forward](https://developer.mozilla.org/en-US/docs/Web/API/History/forward))
|
||||
Future<void> goForward() async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
await _channel.invokeMethod('goForward', args);
|
||||
|
@ -1630,6 +1643,7 @@ class InAppWebViewController
|
|||
///Those changes remain visible to all scripts, regardless of which content world you specify.
|
||||
///For more information about content worlds, see [ContentWorld].
|
||||
///Available on iOS 14.0+.
|
||||
///**NOTE**: not used on Web.
|
||||
///
|
||||
///**NOTE**: This method shouldn't be called in the [WebView.onWebViewCreated] or [WebView.onLoadStart] events,
|
||||
///because, in these events, the [WebView] is not ready to handle it yet.
|
||||
|
@ -1639,6 +1653,7 @@ class InAppWebViewController
|
|||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView ([Official API - WebView.evaluateJavascript](https://developer.android.com/reference/android/webkit/WebView#evaluateJavascript(java.lang.String,%20android.webkit.ValueCallback%3Cjava.lang.String%3E)))
|
||||
///- iOS ([Official API - WKWebView.evaluateJavascript](https://developer.apple.com/documentation/webkit/wkwebview/3656442-evaluatejavascript))
|
||||
///- Web ([Official API - Window.eval](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval?retiredLocale=it))
|
||||
Future<dynamic> evaluateJavascript(
|
||||
{required String source, ContentWorld? contentWorld}) async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
|
|
|
@ -20,6 +20,7 @@ abstract class WebView {
|
|||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView
|
||||
///- iOS
|
||||
///- Web
|
||||
final void Function(InAppWebViewController controller)? onWebViewCreated;
|
||||
|
||||
///Event fired when the [WebView] starts to load an [url].
|
||||
|
@ -27,6 +28,7 @@ abstract class WebView {
|
|||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView ([Official API - WebViewClient.onPageStarted](https://developer.android.com/reference/android/webkit/WebViewClient#onPageStarted(android.webkit.WebView,%20java.lang.String,%20android.graphics.Bitmap)))
|
||||
///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455621-webview))
|
||||
///- Web
|
||||
final void Function(InAppWebViewController controller, Uri? url)? onLoadStart;
|
||||
|
||||
///Event fired when the [WebView] finishes loading an [url].
|
||||
|
@ -34,6 +36,7 @@ abstract class WebView {
|
|||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView ([Official API - WebViewClient.onPageFinished](https://developer.android.com/reference/android/webkit/WebViewClient#onPageFinished(android.webkit.WebView,%20java.lang.String)))
|
||||
///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455629-webview))
|
||||
///- Web ([Official API - Window.onload](https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event))
|
||||
final void Function(InAppWebViewController controller, Uri? url)? onLoadStop;
|
||||
|
||||
///Event fired when the [WebView] encounters an error loading an [url].
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'dart:async';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'dart:html';
|
||||
import 'dart:js' as js;
|
||||
|
||||
import '../in_app_webview/in_app_webview_settings.dart';
|
||||
import '../types.dart';
|
||||
|
@ -16,12 +17,16 @@ class InAppWebViewWebElement {
|
|||
String? initialFile;
|
||||
|
||||
late InAppWebViewSettings settings;
|
||||
late js.JsObject bridgeJsObject;
|
||||
WebHistory webHistory = WebHistory(list: [], currentIndex: -1);
|
||||
|
||||
InAppWebViewWebElement({required int viewId, required BinaryMessenger messenger}) {
|
||||
this._viewId = viewId;
|
||||
this._messenger = messenger;
|
||||
iframe = IFrameElement()
|
||||
..id = 'flutter_inappwebview-$_viewId'
|
||||
..style.height = '100%'
|
||||
..style.width = '100%'
|
||||
..style.border = 'none';
|
||||
|
||||
_channel = MethodChannel(
|
||||
|
@ -32,19 +37,16 @@ class InAppWebViewWebElement {
|
|||
|
||||
this._channel.setMethodCallHandler(handleMethodCall);
|
||||
|
||||
iframe.addEventListener('load', (event) async {
|
||||
var obj = {
|
||||
"url": iframe.src
|
||||
};
|
||||
_channel.invokeMethod("onLoadStart", obj);
|
||||
await Future.delayed(Duration(milliseconds: 100));
|
||||
_channel.invokeMethod("onLoadStop", obj);
|
||||
});
|
||||
bridgeJsObject = js.JsObject.fromBrowserObject(js.context['flutter_inappwebview']);
|
||||
bridgeJsObject['viewId'] = _viewId;
|
||||
bridgeJsObject['iframeId'] = iframe.id;
|
||||
}
|
||||
|
||||
/// Handles method calls over the MethodChannel of this plugin.
|
||||
Future<dynamic> handleMethodCall(MethodCall call) async {
|
||||
switch (call.method) {
|
||||
case "getIFrameId":
|
||||
return iframe.id;
|
||||
case "loadUrl":
|
||||
URLRequest urlRequest = URLRequest.fromMap(call.arguments["urlRequest"].cast<String, dynamic>())!;
|
||||
await _loadUrl(urlRequest: urlRequest);
|
||||
|
@ -61,8 +63,15 @@ class InAppWebViewWebElement {
|
|||
case "reload":
|
||||
await _reload();
|
||||
break;
|
||||
case "getIFrameId":
|
||||
return iframe.id;
|
||||
case "goBack":
|
||||
await _goBack();
|
||||
break;
|
||||
case "goForward":
|
||||
await _goForward();
|
||||
break;
|
||||
case "evaluateJavascript":
|
||||
String source = call.arguments["source"];
|
||||
return await _evaluateJavascript(source: source);
|
||||
default:
|
||||
throw PlatformException(
|
||||
code: 'Unimplemented',
|
||||
|
@ -78,19 +87,13 @@ class InAppWebViewWebElement {
|
|||
if (settings.iframeSandox != null) {
|
||||
iframe.setAttribute("sandbox", settings.iframeSandox ?? "");
|
||||
}
|
||||
var width = settings.iframeWidth ?? iframe.width;
|
||||
if (width == null || width.isEmpty) {
|
||||
width = '100%';
|
||||
}
|
||||
var height = settings.iframeHeight ?? iframe.height;
|
||||
if (height == null || height.isEmpty) {
|
||||
height = '100%';
|
||||
}
|
||||
iframe.width = iframe.style.width = width;
|
||||
iframe.height = iframe.style.height = height;
|
||||
iframe.style.width = settings.iframeWidth ?? iframe.style.width;
|
||||
iframe.style.height = settings.iframeHeight ?? iframe.style.height;
|
||||
iframe.referrerPolicy = settings.iframeReferrerPolicy ?? iframe.referrerPolicy;
|
||||
iframe.name = settings.iframeName ?? iframe.name;
|
||||
iframe.csp = settings.iframeCsp ?? iframe.csp;
|
||||
|
||||
bridgeJsObject.callMethod("prepare");
|
||||
}
|
||||
|
||||
void makeInitialLoad() async {
|
||||
|
@ -129,20 +132,48 @@ class InAppWebViewWebElement {
|
|||
} else {
|
||||
iframe.src = _convertHttpResponseToData(await _makeRequest(urlRequest));
|
||||
}
|
||||
var obj = {
|
||||
"url": iframe.src
|
||||
};
|
||||
_channel.invokeMethod("onLoadStart", obj);
|
||||
}
|
||||
|
||||
Future<void> _loadData({required String data, String mimeType = "text/html"}) async {
|
||||
iframe.src = 'data:$mimeType,' + Uri.encodeFull(data);
|
||||
var obj = {
|
||||
"url": iframe.src
|
||||
};
|
||||
_channel.invokeMethod("onLoadStart", obj);
|
||||
}
|
||||
|
||||
Future<void> _loadFile({required String assetFilePath}) async {
|
||||
iframe.src = assetFilePath;
|
||||
var obj = {
|
||||
"url": iframe.src
|
||||
};
|
||||
_channel.invokeMethod("onLoadStart", obj);
|
||||
}
|
||||
|
||||
Future<void> _reload() async {
|
||||
var src = iframe.src;
|
||||
if (src != null) {
|
||||
iframe.contentWindow?.location.href = src;
|
||||
}
|
||||
bridgeJsObject.callMethod("reload");
|
||||
}
|
||||
|
||||
Future<void> _goBack() async {
|
||||
bridgeJsObject.callMethod("goBack");
|
||||
}
|
||||
|
||||
Future<void> _goForward() async {
|
||||
bridgeJsObject.callMethod("goForward");
|
||||
}
|
||||
|
||||
Future<dynamic> _evaluateJavascript({required String source}) async {
|
||||
return bridgeJsObject.callMethod("evaluateJavascript", [source]);
|
||||
}
|
||||
|
||||
onIFrameLoaded(String url) async {
|
||||
var obj = {
|
||||
"url": url
|
||||
};
|
||||
_channel.invokeMethod("onLoadStop", obj);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@ import 'shims/dart_ui.dart' as ui;
|
|||
|
||||
import 'in_app_web_view_web_element.dart';
|
||||
|
||||
import 'package:js/js.dart';
|
||||
|
||||
/// Builds an iframe based WebView.
|
||||
///
|
||||
/// This is used as the default implementation for [WebView] on web.
|
||||
|
@ -24,6 +26,7 @@ class FlutterInAppWebViewWebPlatform {
|
|||
|
||||
static void registerWith(Registrar registrar) {
|
||||
final pluginInstance = FlutterInAppWebViewWebPlatform(registrar);
|
||||
_nativeCommunication = allowInterop(_dartNativeCommunication);
|
||||
}
|
||||
|
||||
/// Handles method calls over the MethodChannel of this plugin.
|
||||
|
@ -36,4 +39,24 @@ class FlutterInAppWebViewWebPlatform {
|
|||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Allows assigning a function to be callable from `window.flutter_inappwebview.nativeCommunication()`
|
||||
@JS('flutter_inappwebview.nativeCommunication')
|
||||
external set _nativeCommunication(void Function(String method, int viewId, [List? args]) f);
|
||||
|
||||
/// Allows calling the assigned function from Dart as well.
|
||||
@JS()
|
||||
external void nativeCommunication();
|
||||
|
||||
void _dartNativeCommunication(String method, int viewId, [List? args]) {
|
||||
if (WebPlatformManager.webViews.containsKey(viewId)) {
|
||||
var webViewHtmlElement = WebPlatformManager.webViews[viewId] as InAppWebViewWebElement;
|
||||
switch (method) {
|
||||
case 'iframeLoaded':
|
||||
String url = args![0] as String;
|
||||
webViewHtmlElement.onIFrameLoaded(url);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +1,3 @@
|
|||
class WebPlatformManager {
|
||||
abstract class WebPlatformManager {
|
||||
static final Map<int, dynamic> webViews = {};
|
||||
}
|
|
@ -10,6 +10,7 @@ environment:
|
|||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
js: ^0.6.3
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
@ -38,6 +39,7 @@ flutter:
|
|||
assets:
|
||||
- packages/flutter_inappwebview/assets/t_rex_runner/t-rex.html
|
||||
- packages/flutter_inappwebview/assets/t_rex_runner/t-rex.css
|
||||
- packages/flutter_inappwebview/assets/web/web_support.js
|
||||
# To add assets to your plugin package, add an assets section, like this:
|
||||
# assets:
|
||||
# - images/a_dot_burr.jpeg
|
||||
|
|
Loading…
Reference in New Issue