updated web support

This commit is contained in:
Lorenzo Pichilli 2022-04-22 13:39:21 +02:00
parent 24317b22ec
commit 1161380eab
9 changed files with 207 additions and 38 deletions

View File

@ -119,10 +119,10 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
InAppWebView(
key: webViewKey,
// contextMenu: contextMenu,
initialUrlRequest:
URLRequest(url: Uri.parse("https://www.pubnub.com/developers/demos/webrtc/launch/")),
// initialUrlRequest:
// URLRequest(url: Uri.parse(Uri.base.toString().replaceFirst("/#/", "/") + 'page.html')),
// URLRequest(url: Uri.parse("https://www.pubnub.com/developers/demos/webrtc/launch/")),
initialUrlRequest:
URLRequest(url: Uri.parse(Uri.base.toString().replaceFirst("/#/", "/") + 'page.html')),
// initialFile: "assets/index.html",
initialUserScripts: UnmodifiableListView<UserScript>([]),
initialSettings: settings,
@ -221,6 +221,12 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
webViewController?.reload();
},
),
ElevatedButton(
child: Icon(Icons.cancel),
onPressed: () {
webViewController?.stopLoading();
},
),
],
),
])));

View File

@ -0,0 +1,29 @@
<!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>Heavy Page</h1>
<a href="/page.html">Go to page 1</a>
<br>
<p id="infotext">Loading image...</p>
<img src="https://picsum.photos/5000" alt="Loading failed" onerror="this.src = ''; window.infotext != null ? infotext.remove() : null" onload="window.infotext != null ? infotext.remove() : null">
</body>
</html>

View File

@ -1,7 +1,6 @@
<!DOCTYPE html>
<html>
<head>
<base href="$FLUTTER_BASE_HREF">
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
@ -22,5 +21,9 @@
<body>
<h1>Simple Page 2</h1>
<a href="/page.html">Go to page 1</a>
<br>
<a href="/heavy-page.html">Go to heavy-page</a>
<br>
<button onclick="window.history.pushState({}, '', '#test-state-change')">Test state change</button>
</body>
</html>

View File

@ -22,5 +22,7 @@
<body>
<h1>Simple Page 1</h1>
<a href="/page-2.html">Go to page 2</a>
<br>
<a href="/heavy-page.html">Go to heavy-page</a>
</body>
</html>

View File

@ -13,7 +13,44 @@ window.flutter_inappwebview = {
} catch (e) {
console.log(e);
}
window.flutter_inappwebview.nativeCommunication('iframeLoaded', window.flutter_inappwebview.viewId, [url]);
window.flutter_inappwebview.nativeCommunication('onLoadStart', window.flutter_inappwebview.viewId, [url]);
window.flutter_inappwebview.nativeCommunication('onLoadStop', window.flutter_inappwebview.viewId, [url]);
iframe.contentWindow.addEventListener('popstate', function (event) {
var iframeUrl = iframe.src;
try {
iframeUrl = iframe.contentWindow.location.href;
} catch (e) {
console.log(e);
}
window.flutter_inappwebview.nativeCommunication('onUpdateVisitedHistory', window.flutter_inappwebview.viewId, [iframeUrl]);
});
try {
var originalPushState = iframe.contentWindow.history.pushState;
iframe.contentWindow.history.pushState = function (state, unused, url) {
originalPushState.call(iframe.contentWindow.history, state, unused, url);
var iframeUrl = iframe.src;
try {
iframeUrl = iframe.contentWindow.location.href;
} catch (e) {
console.log(e);
}
window.flutter_inappwebview.nativeCommunication('onUpdateVisitedHistory', window.flutter_inappwebview.viewId, [iframeUrl]);
};
var originalReplaceState = iframe.contentWindow.history.replaceState;
iframe.contentWindow.history.replaceState = function (state, unused, url) {
originalReplaceState.call(iframe.contentWindow.history, state, unused, url);
var iframeUrl = iframe.src;
try {
iframeUrl = iframe.contentWindow.location.href;
} catch (e) {
console.log(e);
}
window.flutter_inappwebview.nativeCommunication('onUpdateVisitedHistory', window.flutter_inappwebview.viewId, [iframeUrl]);
};
} catch (e) {
console.log(e);
}
});
}
},
@ -48,6 +85,16 @@ window.flutter_inappwebview = {
}
}
},
goForwardOrForward: function (steps) {
var iframe = window.flutter_inappwebview.iframe;
if (iframe != null) {
try {
iframe.contentWindow.history.go(steps);
} catch (e) {
console.log(e);
}
}
},
evaluateJavascript: function (source) {
var iframe = window.flutter_inappwebview.iframe;
var result = null;
@ -59,5 +106,15 @@ window.flutter_inappwebview = {
}
}
return result;
},
stopLoading: function (steps) {
var iframe = window.flutter_inappwebview.iframe;
if (iframe != null) {
try {
iframe.contentWindow.stop();
} catch (e) {
console.log(e);
}
}
}
};

View File

@ -1586,6 +1586,7 @@ class InAppWebViewController
///**Supported Platforms/Implementations**:
///- Android native WebView ([Official API - WebView.goBackOrForward](https://developer.android.com/reference/android/webkit/WebView#goBackOrForward(int)))
///- iOS ([Official API - WKWebView.go](https://developer.apple.com/documentation/webkit/wkwebview/1414991-go))
///- Web ([Official API - History.go](https://developer.mozilla.org/en-US/docs/Web/API/History/go))
Future<void> goBackOrForward({required int steps}) async {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('steps', () => steps);
@ -1608,6 +1609,7 @@ class InAppWebViewController
///**Supported Platforms/Implementations**:
///- Android native WebView
///- iOS
///- Web
Future<void> goTo({required WebHistoryItem historyItem}) async {
var steps = historyItem.offset;
if (steps != null) {
@ -1620,6 +1622,7 @@ class InAppWebViewController
///**Supported Platforms/Implementations**:
///- Android native WebView
///- iOS
///- Web
Future<bool> isLoading() async {
Map<String, dynamic> args = <String, dynamic>{};
return await _channel.invokeMethod('isLoading', args);

View File

@ -25,6 +25,9 @@ abstract class WebView {
///Event fired when the [WebView] starts to load an [url].
///
///**NOTE**: on Web it will be dispatched at the same time of [onLoadStop] event
///because there isn't any way to capture the real load start event from an iframe.
///
///**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))
@ -380,6 +383,7 @@ abstract class WebView {
///**Supported Platforms/Implementations**:
///- Android native WebView ([Official API - WebViewClient.doUpdateVisitedHistory](https://developer.android.com/reference/android/webkit/WebViewClient#doUpdateVisitedHistory(android.webkit.WebView,%20java.lang.String,%20boolean)))
///- iOS
///- Web
final void Function(
InAppWebViewController controller, Uri? url, bool? isReload)?
onUpdateVisitedHistory;

View File

@ -18,7 +18,7 @@ class InAppWebViewWebElement {
late InAppWebViewSettings settings;
late js.JsObject bridgeJsObject;
WebHistory webHistory = WebHistory(list: [], currentIndex: -1);
bool isLoading = false;
InAppWebViewWebElement({required int viewId, required BinaryMessenger messenger}) {
this._viewId = viewId;
@ -49,29 +49,44 @@ class InAppWebViewWebElement {
return iframe.id;
case "loadUrl":
URLRequest urlRequest = URLRequest.fromMap(call.arguments["urlRequest"].cast<String, dynamic>())!;
await _loadUrl(urlRequest: urlRequest);
await loadUrl(urlRequest: urlRequest);
break;
case "loadData":
String data = call.arguments["data"];
String mimeType = call.arguments["mimeType"];
await _loadData(data: data, mimeType: mimeType);
await loadData(data: data, mimeType: mimeType);
break;
case "loadFile":
String assetFilePath = call.arguments["assetFilePath"];
await _loadFile(assetFilePath: assetFilePath);
await loadFile(assetFilePath: assetFilePath);
break;
case "reload":
await _reload();
await reload();
break;
case "goBack":
await _goBack();
await goBack();
break;
case "goForward":
await _goForward();
await goForward();
break;
case "goBackOrForward":
int steps = call.arguments["steps"];
await goBackOrForward(steps: steps);
break;
case "isLoading":
return isLoading;
case "evaluateJavascript":
String source = call.arguments["source"];
return await _evaluateJavascript(source: source);
return await evaluateJavascript(source: source);
case "stopLoading":
await stopLoading();
break;
case "getSettings":
return await settings.toMap();
case "setSettings":
InAppWebViewSettings newSettings = InAppWebViewSettings.fromMap(call.arguments["settings"].cast<String, dynamic>());
setSettings(newSettings);
break;
default:
throw PlatformException(
code: 'Unimplemented',
@ -98,11 +113,11 @@ class InAppWebViewWebElement {
void makeInitialLoad() async {
if (initialUrlRequest != null) {
_loadUrl(urlRequest: initialUrlRequest!);
loadUrl(urlRequest: initialUrlRequest!);
} else if (initialData != null) {
_loadData(data: initialData!.data, mimeType: initialData!.mimeType);
loadData(data: initialData!.data, mimeType: initialData!.mimeType);
} else if (initialFile != null) {
_loadFile(assetFilePath: initialFile!);
loadFile(assetFilePath: initialFile!);
}
}
@ -125,55 +140,97 @@ class InAppWebViewWebElement {
return 'data:$contentType,' + Uri.encodeFull(httpRequest.responseText ?? '');
}
Future<void> _loadUrl({required URLRequest urlRequest}) async {
Future<void> loadUrl({required URLRequest urlRequest}) async {
if ((urlRequest.method == null || urlRequest.method == "GET") &&
(urlRequest.headers == null || urlRequest.headers!.isEmpty)) {
iframe.src = urlRequest.url.toString();
} 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 {
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 {
Future<void> loadFile({required String assetFilePath}) async {
iframe.src = assetFilePath;
var obj = {
"url": iframe.src
};
_channel.invokeMethod("onLoadStart", obj);
}
Future<void> _reload() async {
Future<void> reload() async {
bridgeJsObject.callMethod("reload");
}
Future<void> _goBack() async {
Future<void> goBack() async {
bridgeJsObject.callMethod("goBack");
}
Future<void> _goForward() async {
Future<void> goForward() async {
bridgeJsObject.callMethod("goForward");
}
Future<dynamic> _evaluateJavascript({required String source}) async {
Future<void> goBackOrForward({required int steps}) async {
bridgeJsObject.callMethod("goBackOrForward", [steps]);
}
Future<dynamic> evaluateJavascript({required String source}) async {
return bridgeJsObject.callMethod("evaluateJavascript", [source]);
}
onIFrameLoaded(String url) async {
Future<void> stopLoading() async {
bridgeJsObject.callMethod("stopLoading");
}
Future<void> setSettings(InAppWebViewSettings newSettings) async {
if (settings.iframeAllow != newSettings.iframeAllow) {
iframe.allow = newSettings.iframeAllow;
}
if (settings.iframeAllowFullscreen != newSettings.iframeAllowFullscreen) {
iframe.allowFullscreen = newSettings.iframeAllowFullscreen;
}
if (settings.iframeSandox != newSettings.iframeSandox) {
iframe.setAttribute("sandbox", newSettings.iframeSandox ?? "");
}
if (settings.iframeWidth != newSettings.iframeWidth) {
iframe.style.width = newSettings.iframeWidth;
}
if (settings.iframeHeight != newSettings.iframeHeight) {
iframe.style.height = newSettings.iframeHeight;
}
if (settings.iframeReferrerPolicy != newSettings.iframeReferrerPolicy) {
iframe.referrerPolicy = newSettings.iframeReferrerPolicy;
}
if (settings.iframeName != newSettings.iframeName) {
iframe.name = newSettings.iframeName;
}
if (settings.iframeCsp != newSettings.iframeCsp) {
iframe.csp = newSettings.iframeCsp;
}
settings = newSettings;
}
onLoadStart(String url) async {
isLoading = true;
var obj = {
"url": url
};
_channel.invokeMethod("onLoadStart", obj);
}
onLoadStop(String url) async {
isLoading = false;
var obj = {
"url": url
};
_channel.invokeMethod("onLoadStop", obj);
}
onUpdateVisitedHistory(String url) async {
var obj = {
"url": url
};
_channel.invokeMethod("onUpdateVisitedHistory", obj);
}
}

View File

@ -53,9 +53,17 @@ void _dartNativeCommunication(String method, int viewId, [List? args]) {
if (WebPlatformManager.webViews.containsKey(viewId)) {
var webViewHtmlElement = WebPlatformManager.webViews[viewId] as InAppWebViewWebElement;
switch (method) {
case 'iframeLoaded':
case 'onLoadStart':
String url = args![0] as String;
webViewHtmlElement.onIFrameLoaded(url);
webViewHtmlElement.onLoadStart(url);
break;
case 'onLoadStop':
String url = args![0] as String;
webViewHtmlElement.onLoadStop(url);
break;
case 'onUpdateVisitedHistory':
String url = args![0] as String;
webViewHtmlElement.onUpdateVisitedHistory(url);
break;
}
}