added headless and cookie manager web support
This commit is contained in:
parent
d1a4ff1829
commit
bb33ec2362
@ -24,8 +24,6 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
|
||||
allowsInlineMediaPlayback: true,
|
||||
iframeAllow: "camera; microphone",
|
||||
iframeAllowFullscreen: true,
|
||||
disableVerticalScroll: true,
|
||||
disableHorizontalScroll: true,
|
||||
);
|
||||
|
||||
PullToRefreshController? pullToRefreshController;
|
||||
@ -123,7 +121,7 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
|
||||
initialUserScripts: UnmodifiableListView<UserScript>([]),
|
||||
initialSettings: settings,
|
||||
pullToRefreshController: pullToRefreshController,
|
||||
onWebViewCreated: (controller) {
|
||||
onWebViewCreated: (controller) async {
|
||||
webViewController = controller;
|
||||
},
|
||||
onLoadStart: (controller, url) async {
|
||||
@ -162,17 +160,11 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
|
||||
return NavigationActionPolicy.ALLOW;
|
||||
},
|
||||
onLoadStop: (controller, url) async {
|
||||
print("onLoadStop");
|
||||
pullToRefreshController?.endRefreshing();
|
||||
setState(() {
|
||||
this.url = url.toString();
|
||||
urlController.text = this.url;
|
||||
});
|
||||
await Future.delayed(Duration(seconds: 2));
|
||||
await controller.setSettings(settings: settings.copy()
|
||||
..disableVerticalScroll = false
|
||||
..disableHorizontalScroll = false
|
||||
);
|
||||
},
|
||||
onLoadError: (controller, url, code, message) {
|
||||
pullToRefreshController?.endRefreshing();
|
||||
|
@ -1,337 +1,365 @@
|
||||
window.flutter_inappwebview = {
|
||||
viewId: null,
|
||||
iframeId: null,
|
||||
iframe: null,
|
||||
windowAutoincrementId: 0,
|
||||
windows: {},
|
||||
isFullscreen: false,
|
||||
documentTitle: null,
|
||||
functionMap: {},
|
||||
settings: {},
|
||||
prepare: function (settings) {
|
||||
window.flutter_inappwebview.settings = settings;
|
||||
var iframe = document.getElementById(window.flutter_inappwebview.iframeId);
|
||||
webViews: {},
|
||||
createFlutterInAppWebView: function(viewId, iframeId) {
|
||||
var webView = {
|
||||
viewId: viewId,
|
||||
iframeId: iframeId,
|
||||
iframe: null,
|
||||
windowAutoincrementId: 0,
|
||||
windows: {},
|
||||
isFullscreen: false,
|
||||
documentTitle: null,
|
||||
functionMap: {},
|
||||
settings: {},
|
||||
disableContextMenuHandler: function(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
},
|
||||
prepare: function (settings) {
|
||||
webView.settings = settings;
|
||||
var iframe = document.getElementById(iframeId);
|
||||
|
||||
document.addEventListener('fullscreenchange', function(event) {
|
||||
// document.fullscreenElement will point to the element that
|
||||
// is in fullscreen mode if there is one. If there isn't one,
|
||||
// the value of the property is null.
|
||||
if (document.fullscreenElement && document.fullscreenElement.id == window.flutter_inappwebview.iframeId) {
|
||||
window.flutter_inappwebview.isFullscreen = true;
|
||||
window.flutter_inappwebview.nativeCommunication('onEnterFullscreen', window.flutter_inappwebview.viewId);
|
||||
} else if (!document.fullscreenElement && window.flutter_inappwebview.isFullscreen) {
|
||||
window.flutter_inappwebview.isFullscreen = false;
|
||||
window.flutter_inappwebview.nativeCommunication('onExitFullscreen', window.flutter_inappwebview.viewId);
|
||||
} else {
|
||||
window.flutter_inappwebview.isFullscreen = false;
|
||||
}
|
||||
});
|
||||
document.addEventListener('fullscreenchange', function(event) {
|
||||
// document.fullscreenElement will point to the element that
|
||||
// is in fullscreen mode if there is one. If there isn't one,
|
||||
// the value of the property is null.
|
||||
if (document.fullscreenElement && document.fullscreenElement.id == iframeId) {
|
||||
webView.isFullscreen = true;
|
||||
window.flutter_inappwebview.nativeCommunication('onEnterFullscreen', viewId);
|
||||
} else if (!document.fullscreenElement && webView.isFullscreen) {
|
||||
webView.isFullscreen = false;
|
||||
window.flutter_inappwebview.nativeCommunication('onExitFullscreen', viewId);
|
||||
} else {
|
||||
webView.isFullscreen = false;
|
||||
}
|
||||
});
|
||||
|
||||
if (iframe != null) {
|
||||
window.flutter_inappwebview.iframe = iframe;
|
||||
iframe.addEventListener('load', function (event) {
|
||||
window.flutter_inappwebview.windowAutoincrementId = 0;
|
||||
window.flutter_inappwebview.windows = {};
|
||||
if (iframe != null) {
|
||||
webView.iframe = iframe;
|
||||
iframe.addEventListener('load', function (event) {
|
||||
webView.windowAutoincrementId = 0;
|
||||
webView.windows = {};
|
||||
|
||||
var url = iframe.src;
|
||||
try {
|
||||
url = iframe.contentWindow.location.href;
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
window.flutter_inappwebview.nativeCommunication('onLoadStart', window.flutter_inappwebview.viewId, [url]);
|
||||
var url = iframe.src;
|
||||
try {
|
||||
url = iframe.contentWindow.location.href;
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
window.flutter_inappwebview.nativeCommunication('onLoadStart', viewId, [url]);
|
||||
|
||||
try {
|
||||
var oldLogs = {
|
||||
'log': iframe.contentWindow.console.log,
|
||||
'debug': iframe.contentWindow.console.debug,
|
||||
'error': iframe.contentWindow.console.error,
|
||||
'info': iframe.contentWindow.console.info,
|
||||
'warn': iframe.contentWindow.console.warn
|
||||
};
|
||||
for (var k in oldLogs) {
|
||||
(function(oldLog) {
|
||||
iframe.contentWindow.console[oldLog] = function() {
|
||||
var message = '';
|
||||
for (var i in arguments) {
|
||||
if (message == '') {
|
||||
message += arguments[i];
|
||||
} else {
|
||||
message += ' ' + arguments[i];
|
||||
try {
|
||||
var oldLogs = {
|
||||
'log': iframe.contentWindow.console.log,
|
||||
'debug': iframe.contentWindow.console.debug,
|
||||
'error': iframe.contentWindow.console.error,
|
||||
'info': iframe.contentWindow.console.info,
|
||||
'warn': iframe.contentWindow.console.warn
|
||||
};
|
||||
for (var k in oldLogs) {
|
||||
(function(oldLog) {
|
||||
iframe.contentWindow.console[oldLog] = function() {
|
||||
var message = '';
|
||||
for (var i in arguments) {
|
||||
if (message == '') {
|
||||
message += arguments[i];
|
||||
} else {
|
||||
message += ' ' + arguments[i];
|
||||
}
|
||||
}
|
||||
oldLogs[oldLog].call(iframe.contentWindow.console, ...arguments);
|
||||
window.flutter_inappwebview.nativeCommunication('onConsoleMessage', viewId, [oldLog, message]);
|
||||
}
|
||||
})(k);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
oldLogs[oldLog].call(iframe.contentWindow.console, ...arguments);
|
||||
window.flutter_inappwebview.nativeCommunication('onConsoleMessage', window.flutter_inappwebview.viewId, [oldLog, message]);
|
||||
window.flutter_inappwebview.nativeCommunication('onUpdateVisitedHistory', 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', viewId, [iframeUrl]);
|
||||
};
|
||||
|
||||
var originalOpen = iframe.contentWindow.open;
|
||||
iframe.contentWindow.open = function (url, target, windowFeatures) {
|
||||
var newWindow = originalOpen.call(iframe.contentWindow, ...arguments);
|
||||
var windowId = webView.windowAutoincrementId;
|
||||
webView.windowAutoincrementId++;
|
||||
webView.windows[windowId] = newWindow;
|
||||
window.flutter_inappwebview.nativeCommunication('onCreateWindow', viewId, [windowId, url, target, windowFeatures]).then(function(){}, function(handledByClient) {
|
||||
if (handledByClient) {
|
||||
newWindow.close();
|
||||
}
|
||||
});
|
||||
return newWindow;
|
||||
};
|
||||
|
||||
var originalPrint = iframe.contentWindow.print;
|
||||
iframe.contentWindow.print = function () {
|
||||
var iframeUrl = iframe.src;
|
||||
try {
|
||||
iframeUrl = iframe.contentWindow.location.href;
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
window.flutter_inappwebview.nativeCommunication('onPrint', viewId, [iframeUrl]);
|
||||
originalPrint.call(iframe.contentWindow);
|
||||
};
|
||||
|
||||
webView.functionMap = {
|
||||
"window.open": iframe.contentWindow.open,
|
||||
"window.print": iframe.contentWindow.print,
|
||||
"window.history.pushState": iframe.contentWindow.history.pushState,
|
||||
"window.history.replaceState": iframe.contentWindow.history.replaceState,
|
||||
}
|
||||
})(k);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
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;
|
||||
var initialTitle = iframe.contentDocument.title;
|
||||
webView.documentTitle = initialTitle;
|
||||
window.flutter_inappwebview.nativeCommunication('onTitleChanged', viewId, [initialTitle]);
|
||||
new MutationObserver(function(mutations) {
|
||||
var title = mutations[0].target.nodeValue;
|
||||
if (title != webView.documentTitle) {
|
||||
webView.documentTitle = title;
|
||||
window.flutter_inappwebview.nativeCommunication('onTitleChanged', viewId, [title]);
|
||||
}
|
||||
}).observe(
|
||||
iframe.contentDocument.querySelector('title'),
|
||||
{ subtree: true, characterData: true, childList: true }
|
||||
);
|
||||
|
||||
var oldPixelRatio = iframe.contentWindow.devicePixelRatio;
|
||||
iframe.contentWindow.addEventListener('resize', function (e) {
|
||||
var newPixelRatio = iframe.contentWindow.devicePixelRatio;
|
||||
if(newPixelRatio !== oldPixelRatio){
|
||||
window.flutter_inappwebview.nativeCommunication('onZoomScaleChanged', viewId, [oldPixelRatio, newPixelRatio]);
|
||||
oldPixelRatio = newPixelRatio;
|
||||
}
|
||||
});
|
||||
|
||||
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', viewId, [iframeUrl]);
|
||||
});
|
||||
|
||||
iframe.contentWindow.addEventListener('scroll', function (event) {
|
||||
var x = 0;
|
||||
var y = 0;
|
||||
try {
|
||||
x = iframe.contentWindow.scrollX;
|
||||
y = iframe.contentWindow.scrollY;
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
window.flutter_inappwebview.nativeCommunication('onScrollChanged', viewId, [x, y]);
|
||||
});
|
||||
|
||||
iframe.contentWindow.addEventListener('focus', function (event) {
|
||||
window.flutter_inappwebview.nativeCommunication('onWindowFocus', viewId);
|
||||
});
|
||||
|
||||
iframe.contentWindow.addEventListener('blur', function (event) {
|
||||
window.flutter_inappwebview.nativeCommunication('onWindowBlur', viewId);
|
||||
});
|
||||
} 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]);
|
||||
};
|
||||
|
||||
var originalOpen = iframe.contentWindow.open;
|
||||
iframe.contentWindow.open = function (url, target, windowFeatures) {
|
||||
var newWindow = originalOpen.call(iframe.contentWindow, ...arguments);
|
||||
var windowId = window.flutter_inappwebview.windowAutoincrementId;
|
||||
window.flutter_inappwebview.windowAutoincrementId++;
|
||||
window.flutter_inappwebview.windows[windowId] = newWindow;
|
||||
window.flutter_inappwebview.nativeCommunication('onCreateWindow', window.flutter_inappwebview.viewId, [windowId, url, target, windowFeatures]).then(function(){}, function(handledByClient) {
|
||||
if (handledByClient) {
|
||||
newWindow.close();
|
||||
if (!webView.settings.javaScriptCanOpenWindowsAutomatically) {
|
||||
iframe.contentWindow.open = function () {
|
||||
throw new Error('JavaScript cannot open windows automatically');
|
||||
};
|
||||
}
|
||||
});
|
||||
return newWindow;
|
||||
};
|
||||
|
||||
var originalPrint = iframe.contentWindow.print;
|
||||
iframe.contentWindow.print = function () {
|
||||
var iframeUrl = iframe.src;
|
||||
try {
|
||||
iframeUrl = iframe.contentWindow.location.href;
|
||||
if (!webView.settings.verticalScrollBarEnabled && !webView.settings.horizontalScrollBarEnabled) {
|
||||
var style = iframe.contentDocument.createElement('style');
|
||||
style.id = "settings.verticalScrollBarEnabled-settings.horizontalScrollBarEnabled";
|
||||
style.innerHTML = "body::-webkit-scrollbar { width: 0px; height: 0px; }";
|
||||
iframe.contentDocument.head.append(style);
|
||||
}
|
||||
|
||||
if (webView.settings.disableVerticalScroll) {
|
||||
var style = iframe.contentDocument.createElement('style');
|
||||
style.id = "settings.disableVerticalScroll";
|
||||
style.innerHTML = "body { overflow-y: hidden; }";
|
||||
iframe.contentDocument.head.append(style);
|
||||
}
|
||||
|
||||
if (webView.settings.disableHorizontalScroll) {
|
||||
var style = iframe.contentDocument.createElement('style');
|
||||
style.id = "settings.disableHorizontalScroll";
|
||||
style.innerHTML = "body { overflow-x: hidden; }";
|
||||
iframe.contentDocument.head.append(style);
|
||||
}
|
||||
|
||||
if (webView.settings.disableContextMenu) {
|
||||
iframe.contentWindow.addEventListener('contextmenu', webView.disableContextMenuHandler);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
window.flutter_inappwebview.nativeCommunication('onPrint', window.flutter_inappwebview.viewId, [iframeUrl]);
|
||||
originalPrint.call(iframe.contentWindow);
|
||||
};
|
||||
|
||||
window.flutter_inappwebview.functionMap = {
|
||||
"window.open": iframe.contentWindow.open,
|
||||
"window.print": iframe.contentWindow.print,
|
||||
"window.history.pushState": iframe.contentWindow.history.pushState,
|
||||
"window.history.replaceState": iframe.contentWindow.history.replaceState,
|
||||
}
|
||||
|
||||
var initialTitle = iframe.contentDocument.title;
|
||||
window.flutter_inappwebview.documentTitle = initialTitle;
|
||||
window.flutter_inappwebview.nativeCommunication('onTitleChanged', window.flutter_inappwebview.viewId, [initialTitle]);
|
||||
new MutationObserver(function(mutations) {
|
||||
var title = mutations[0].target.nodeValue;
|
||||
if (title != window.flutter_inappwebview.documentTitle) {
|
||||
window.flutter_inappwebview.documentTitle = title;
|
||||
window.flutter_inappwebview.nativeCommunication('onTitleChanged', window.flutter_inappwebview.viewId, [title]);
|
||||
}
|
||||
}).observe(
|
||||
iframe.contentDocument.querySelector('title'),
|
||||
{ subtree: true, characterData: true, childList: true }
|
||||
);
|
||||
|
||||
var oldPixelRatio = iframe.contentWindow.devicePixelRatio;
|
||||
iframe.contentWindow.addEventListener('resize', function (e) {
|
||||
var newPixelRatio = iframe.contentWindow.devicePixelRatio;
|
||||
if(newPixelRatio !== oldPixelRatio){
|
||||
window.flutter_inappwebview.nativeCommunication('onZoomScaleChanged', window.flutter_inappwebview.viewId, [oldPixelRatio, newPixelRatio]);
|
||||
oldPixelRatio = newPixelRatio;
|
||||
}
|
||||
window.flutter_inappwebview.nativeCommunication('onLoadStop', 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]);
|
||||
});
|
||||
|
||||
iframe.contentWindow.addEventListener('scroll', function (event) {
|
||||
var x = 0;
|
||||
var y = 0;
|
||||
try {
|
||||
x = iframe.contentWindow.scrollX;
|
||||
y = iframe.contentWindow.scrollY;
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
window.flutter_inappwebview.nativeCommunication('onScrollChanged', window.flutter_inappwebview.viewId, [x, y]);
|
||||
});
|
||||
|
||||
iframe.contentWindow.addEventListener('focus', function (event) {
|
||||
window.flutter_inappwebview.nativeCommunication('onWindowFocus', window.flutter_inappwebview.viewId);
|
||||
});
|
||||
|
||||
iframe.contentWindow.addEventListener('blur', function (event) {
|
||||
window.flutter_inappwebview.nativeCommunication('onWindowBlur', window.flutter_inappwebview.viewId);
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
},
|
||||
setSettings: function (newSettings) {
|
||||
var iframe = webView.iframe;
|
||||
try {
|
||||
|
||||
if (!window.flutter_inappwebview.settings.javaScriptCanOpenWindowsAutomatically) {
|
||||
iframe.contentWindow.open = function () {
|
||||
throw new Error('JavaScript cannot open windows automatically');
|
||||
};
|
||||
if (webView.settings.javaScriptCanOpenWindowsAutomatically != newSettings.javaScriptCanOpenWindowsAutomatically) {
|
||||
if (!newSettings.javaScriptCanOpenWindowsAutomatically) {
|
||||
iframe.contentWindow.open = function () {
|
||||
throw new Error('JavaScript cannot open windows automatically');
|
||||
};
|
||||
} else {
|
||||
iframe.contentWindow.open = webView.functionMap["window.open"];
|
||||
}
|
||||
}
|
||||
|
||||
if (!window.flutter_inappwebview.settings.verticalScrollBarEnabled && !window.flutter_inappwebview.settings.horizontalScrollBarEnabled) {
|
||||
var style = iframe.contentDocument.createElement('style');
|
||||
style.id = "settings.verticalScrollBarEnabled-settings.horizontalScrollBarEnabled";
|
||||
style.innerHTML = "body::-webkit-scrollbar { width: 0px; height: 0px; }";
|
||||
iframe.contentDocument.head.append(style);
|
||||
if (webView.settings.verticalScrollBarEnabled != newSettings.verticalScrollBarEnabled &&
|
||||
webView.settings.horizontalScrollBarEnabled != newSettings.horizontalScrollBarEnabled) {
|
||||
if (!newSettings.verticalScrollBarEnabled && !newSettings.horizontalScrollBarEnabled) {
|
||||
var style = iframe.contentDocument.createElement('style');
|
||||
style.id = "settings.verticalScrollBarEnabled-settings.horizontalScrollBarEnabled";
|
||||
style.innerHTML = "body::-webkit-scrollbar { width: 0px; height: 0px; }";
|
||||
iframe.contentDocument.head.append(style);
|
||||
} else {
|
||||
var styleElement = iframe.contentDocument.getElementById("settings.verticalScrollBarEnabled-settings.horizontalScrollBarEnabled");
|
||||
if (styleElement) { styleElement.remove() }
|
||||
}
|
||||
}
|
||||
|
||||
if (window.flutter_inappwebview.settings.disableVerticalScroll) {
|
||||
var style = iframe.contentDocument.createElement('style');
|
||||
style.id = "settings.disableVerticalScroll";
|
||||
style.innerHTML = "body { overflow-y: hidden; }";
|
||||
iframe.contentDocument.head.append(style);
|
||||
if (webView.settings.disableVerticalScroll != newSettings.disableVerticalScroll) {
|
||||
if (newSettings.disableVerticalScroll) {
|
||||
var style = iframe.contentDocument.createElement('style');
|
||||
style.id = "settings.disableVerticalScroll";
|
||||
style.innerHTML = "body { overflow-y: hidden; }";
|
||||
iframe.contentDocument.head.append(style);
|
||||
} else {
|
||||
var styleElement = iframe.contentDocument.getElementById("settings.disableVerticalScroll");
|
||||
if (styleElement) { styleElement.remove() }
|
||||
}
|
||||
}
|
||||
|
||||
if (window.flutter_inappwebview.settings.disableHorizontalScroll) {
|
||||
var style = iframe.contentDocument.createElement('style');
|
||||
style.id = "settings.disableHorizontalScroll";
|
||||
style.innerHTML = "body { overflow-x: hidden; }";
|
||||
iframe.contentDocument.head.append(style);
|
||||
if (webView.settings.disableHorizontalScroll != newSettings.disableHorizontalScroll) {
|
||||
if (newSettings.disableHorizontalScroll) {
|
||||
var style = iframe.contentDocument.createElement('style');
|
||||
style.id = "settings.disableHorizontalScroll";
|
||||
style.innerHTML = "body { overflow-x: hidden; }";
|
||||
iframe.contentDocument.head.append(style);
|
||||
} else {
|
||||
var styleElement = iframe.contentDocument.getElementById("settings.disableHorizontalScroll");
|
||||
if (styleElement) { styleElement.remove() }
|
||||
}
|
||||
}
|
||||
|
||||
if (webView.settings.disableContextMenu != newSettings.disableContextMenu) {
|
||||
if (newSettings.disableContextMenu) {
|
||||
iframe.contentWindow.addEventListener('contextmenu', webView.disableContextMenuHandler);
|
||||
} else {
|
||||
iframe.contentWindow.removeEventListener('contextmenu', webView.disableContextMenuHandler);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
window.flutter_inappwebview.nativeCommunication('onLoadStop', window.flutter_inappwebview.viewId, [url]);
|
||||
});
|
||||
}
|
||||
},
|
||||
setSettings: function (newSettings) {
|
||||
var iframe = window.flutter_inappwebview.iframe;
|
||||
try {
|
||||
if (window.flutter_inappwebview.settings.javaScriptCanOpenWindowsAutomatically != newSettings.javaScriptCanOpenWindowsAutomatically) {
|
||||
if (!newSettings.javaScriptCanOpenWindowsAutomatically) {
|
||||
iframe.contentWindow.open = function () {
|
||||
throw new Error('JavaScript cannot open windows automatically');
|
||||
};
|
||||
} else {
|
||||
iframe.contentWindow.open = window.flutter_inappwebview.functionMap["window.open"];
|
||||
webView.settings = newSettings;
|
||||
},
|
||||
reload: function () {
|
||||
var iframe = webView.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 = webView.iframe;
|
||||
if (iframe != null) {
|
||||
try {
|
||||
iframe.contentWindow.history.back();
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
},
|
||||
goForward: function () {
|
||||
var iframe = webView.iframe;
|
||||
if (iframe != null) {
|
||||
try {
|
||||
iframe.contentWindow.history.forward();
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
},
|
||||
goForwardOrForward: function (steps) {
|
||||
var iframe = webView.iframe;
|
||||
if (iframe != null) {
|
||||
try {
|
||||
iframe.contentWindow.history.go(steps);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
},
|
||||
evaluateJavascript: function (source) {
|
||||
var iframe = webView.iframe;
|
||||
var result = null;
|
||||
if (iframe != null) {
|
||||
try {
|
||||
result = JSON.stringify(iframe.contentWindow.eval(source));
|
||||
} catch (e) {}
|
||||
}
|
||||
return result;
|
||||
},
|
||||
stopLoading: function (steps) {
|
||||
var iframe = webView.iframe;
|
||||
if (iframe != null) {
|
||||
try {
|
||||
iframe.contentWindow.stop();
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (window.flutter_inappwebview.settings.verticalScrollBarEnabled != newSettings.verticalScrollBarEnabled &&
|
||||
window.flutter_inappwebview.settings.horizontalScrollBarEnabled != newSettings.horizontalScrollBarEnabled) {
|
||||
if (!newSettings.verticalScrollBarEnabled && !newSettings.horizontalScrollBarEnabled) {
|
||||
var style = iframe.contentDocument.createElement('style');
|
||||
style.id = "settings.verticalScrollBarEnabled-settings.horizontalScrollBarEnabled";
|
||||
style.innerHTML = "body::-webkit-scrollbar { width: 0px; height: 0px; }";
|
||||
iframe.contentDocument.head.append(style);
|
||||
} else {
|
||||
var styleElement = iframe.contentDocument.getElementById("settings.verticalScrollBarEnabled-settings.horizontalScrollBarEnabled");
|
||||
if (styleElement) { styleElement.remove() }
|
||||
}
|
||||
}
|
||||
|
||||
if (window.flutter_inappwebview.settings.disableVerticalScroll != newSettings.disableVerticalScroll) {
|
||||
if (newSettings.disableVerticalScroll) {
|
||||
var style = iframe.contentDocument.createElement('style');
|
||||
style.id = "settings.disableVerticalScroll";
|
||||
style.innerHTML = "body { overflow-y: hidden; }";
|
||||
iframe.contentDocument.head.append(style);
|
||||
} else {
|
||||
var styleElement = iframe.contentDocument.getElementById("settings.disableVerticalScroll");
|
||||
if (styleElement) { styleElement.remove() }
|
||||
}
|
||||
}
|
||||
|
||||
if (window.flutter_inappwebview.settings.disableHorizontalScroll != newSettings.disableHorizontalScroll) {
|
||||
if (newSettings.disableHorizontalScroll) {
|
||||
var style = iframe.contentDocument.createElement('style');
|
||||
style.id = "settings.disableHorizontalScroll";
|
||||
style.innerHTML = "body { overflow-x: hidden; }";
|
||||
iframe.contentDocument.head.append(style);
|
||||
} else {
|
||||
var styleElement = iframe.contentDocument.getElementById("settings.disableHorizontalScroll");
|
||||
if (styleElement) { styleElement.remove() }
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
window.flutter_inappwebview.settings = newSettings;
|
||||
return webView;
|
||||
},
|
||||
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);
|
||||
}
|
||||
}
|
||||
},
|
||||
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;
|
||||
if (iframe != null) {
|
||||
try {
|
||||
result = JSON.stringify(iframe.contentWindow.eval(source));
|
||||
} catch (e) {}
|
||||
}
|
||||
return result;
|
||||
},
|
||||
stopLoading: function (steps) {
|
||||
var iframe = window.flutter_inappwebview.iframe;
|
||||
if (iframe != null) {
|
||||
try {
|
||||
iframe.contentWindow.stop();
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
getCookieExpirationDate: function(timestamp) {
|
||||
return (new Date(timestamp)).toUTCString();
|
||||
}
|
||||
};
|
@ -36,6 +36,10 @@ class ChromeSafariBrowserNotOpenedException implements Exception {
|
||||
///**NOTE**: If you want to use the `ChromeSafariBrowser` class on Android 11+ you need to specify your app querying for
|
||||
///`android.support.customtabs.action.CustomTabsService` in your `AndroidManifest.xml`
|
||||
///(you can read more about it here: https://developers.google.com/web/android/custom-tabs/best-practices#applications_targeting_android_11_api_level_30_or_above).
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView
|
||||
///- iOS
|
||||
class ChromeSafariBrowser {
|
||||
///View ID used internally.
|
||||
late final String id;
|
||||
|
@ -6,6 +6,10 @@ import 'types.dart';
|
||||
///Class that represents the WebView context menu. It used by [WebView.contextMenu].
|
||||
///
|
||||
///**NOTE**: To make it work properly on Android, JavaScript should be enabled!
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView
|
||||
///- iOS
|
||||
class ContextMenu {
|
||||
///Event fired when the context menu for this WebView is being built.
|
||||
///
|
||||
|
@ -17,6 +17,15 @@ import 'types.dart';
|
||||
///**NOTE for iOS below 11.0 (LIMITED SUPPORT!)**: in this case, almost all of the methods ([CookieManager.deleteAllCookies] and [CookieManager.getAllCookies] are not supported!)
|
||||
///has been implemented using JavaScript because there is no other way to work with them on iOS below 11.0.
|
||||
///See https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies for JavaScript restrictions.
|
||||
///
|
||||
///**NOTE for Web (LIMITED SUPPORT!)**: in this case, almost all of the methods ([CookieManager.deleteAllCookies] and [CookieManager.getAllCookies] are not supported!)
|
||||
///has been implemented using JavaScript, so all methods will have effect only if the iframe has the same origin.
|
||||
///See https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies for JavaScript restrictions.
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView
|
||||
///- iOS
|
||||
///- Web
|
||||
class CookieManager {
|
||||
static CookieManager? _instance;
|
||||
static const MethodChannel _channel = const MethodChannel(
|
||||
@ -48,15 +57,20 @@ class CookieManager {
|
||||
///The default value of [path] is `"/"`.
|
||||
///If [domain] is `null`, its default value will be the domain name of [url].
|
||||
///
|
||||
///[iosBelow11WebViewController] could be used if you need to set a session-only cookie using JavaScript (so [isHttpOnly] cannot be set, see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies)
|
||||
///on the current URL of the [WebView] managed by that controller when you need to target iOS below 11. In this case the [url] parameter is ignored.
|
||||
///[webViewController] could be used if you need to set a session-only cookie using JavaScript (so [isHttpOnly] cannot be set, see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies)
|
||||
///on the current URL of the [WebView] managed by that controller when you need to target iOS below 11 and Web platform. In this case the [url] parameter is ignored.
|
||||
///
|
||||
///**NOTE for iOS below 11.0**: If [iosBelow11WebViewController] is `null` or JavaScript is disabled for it, it will try to use a [HeadlessInAppWebView]
|
||||
///**NOTE for iOS below 11.0**: If [webViewController] is `null` or JavaScript is disabled for it, it will try to use a [HeadlessInAppWebView]
|
||||
///to set the cookie (session-only cookie won't work! In that case, you should set also [expiresDate] or [maxAge]).
|
||||
///
|
||||
///**NOTE for Web**: this method will have effect only if the iframe has the same origin.
|
||||
///If [webViewController] is `null` or JavaScript is disabled for it, it will try to use a [HeadlessInAppWebView]
|
||||
///to set the cookie (session-only cookie won't work! In that case, you should set also [expiresDate] or [maxAge]).
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView ([Official API - CookieManager.setCookie](https://developer.android.com/reference/android/webkit/CookieManager#setCookie(java.lang.String,%20java.lang.String,%20android.webkit.ValueCallback%3Cjava.lang.Boolean%3E)))
|
||||
///- iOS ([Official API - WKHTTPCookieStore.setCookie](https://developer.apple.com/documentation/webkit/wkhttpcookiestore/2882007-setcookie))
|
||||
///- Web
|
||||
Future<void> setCookie(
|
||||
{required Uri url,
|
||||
required String name,
|
||||
@ -68,19 +82,26 @@ class CookieManager {
|
||||
bool? isSecure,
|
||||
bool? isHttpOnly,
|
||||
HTTPCookieSameSitePolicy? sameSite,
|
||||
InAppWebViewController? iosBelow11WebViewController}) async {
|
||||
@Deprecated("Use webViewController instead") InAppWebViewController? iosBelow11WebViewController,
|
||||
InAppWebViewController? webViewController}) async {
|
||||
if (domain == null) domain = _getDomainName(url);
|
||||
|
||||
webViewController = webViewController ?? iosBelow11WebViewController;
|
||||
|
||||
assert(url.toString().isNotEmpty);
|
||||
assert(name.isNotEmpty);
|
||||
assert(value.isNotEmpty);
|
||||
assert(domain.isNotEmpty);
|
||||
assert(path.isNotEmpty);
|
||||
|
||||
if (defaultTargetPlatform == TargetPlatform.iOS) {
|
||||
var platformUtil = PlatformUtil();
|
||||
var version = double.tryParse(await platformUtil.getSystemVersion());
|
||||
if (version != null && version < 11.0) {
|
||||
if (defaultTargetPlatform == TargetPlatform.iOS || kIsWeb) {
|
||||
var shouldUseJavascript = kIsWeb;
|
||||
if (defaultTargetPlatform == TargetPlatform.iOS && !kIsWeb) {
|
||||
var platformUtil = PlatformUtil.instance();
|
||||
var version = double.tryParse(await platformUtil.getSystemVersion());
|
||||
shouldUseJavascript = version != null && version < 11.0;
|
||||
}
|
||||
if (shouldUseJavascript) {
|
||||
await _setCookieWithJavaScript(
|
||||
url: url,
|
||||
name: name,
|
||||
@ -91,7 +112,7 @@ class CookieManager {
|
||||
maxAge: maxAge,
|
||||
isSecure: isSecure,
|
||||
sameSite: sameSite,
|
||||
webViewController: iosBelow11WebViewController);
|
||||
webViewController: webViewController);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -161,28 +182,40 @@ class CookieManager {
|
||||
|
||||
///Gets all the cookies for the given [url].
|
||||
///
|
||||
///[iosBelow11WebViewController] is used for getting the cookies (also session-only cookies) using JavaScript (cookies with `isHttpOnly` enabled cannot be found, see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies)
|
||||
///from the current context of the [WebView] managed by that controller when you need to target iOS below 11. JavaScript must be enabled in order to work.
|
||||
///[webViewController] is used for getting the cookies (also session-only cookies) using JavaScript (cookies with `isHttpOnly` enabled cannot be found, see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies)
|
||||
///from the current context of the [WebView] managed by that controller when you need to target iOS below 11 and Web platform. JavaScript must be enabled in order to work.
|
||||
///In this case the [url] parameter is ignored.
|
||||
///
|
||||
///**NOTE for iOS below 11.0**: All the cookies returned this way will have all the properties to `null` except for [Cookie.name] and [Cookie.value].
|
||||
///If [iosBelow11WebViewController] is `null` or JavaScript is disabled for it, it will try to use a [HeadlessInAppWebView]
|
||||
///If [webViewController] is `null` or JavaScript is disabled for it, it will try to use a [HeadlessInAppWebView]
|
||||
///to get the cookies (session-only cookies and cookies with `isHttpOnly` enabled won't be found!).
|
||||
///
|
||||
///**NOTE for Web**: this method will have effect only if the iframe has the same origin.
|
||||
///If [webViewController] is `null` or JavaScript is disabled for it, it will try to use a [HeadlessInAppWebView]
|
||||
///to get the cookies (session-only cookies and cookies with `isHttpOnly` enabled won't be found!).
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView ([Official API - CookieManager.getCookie](https://developer.android.com/reference/android/webkit/CookieManager#getCookie(java.lang.String)))
|
||||
///- iOS ([Official API - WKHTTPCookieStore.getAllCookies](https://developer.apple.com/documentation/webkit/wkhttpcookiestore/2882005-getallcookies))
|
||||
///- Web
|
||||
Future<List<Cookie>> getCookies(
|
||||
{required Uri url,
|
||||
InAppWebViewController? iosBelow11WebViewController}) async {
|
||||
@Deprecated("Use webViewController instead") InAppWebViewController? iosBelow11WebViewController,
|
||||
InAppWebViewController? webViewController}) async {
|
||||
assert(url.toString().isNotEmpty);
|
||||
|
||||
if (defaultTargetPlatform == TargetPlatform.iOS) {
|
||||
var platformUtil = PlatformUtil();
|
||||
var version = double.tryParse(await platformUtil.getSystemVersion());
|
||||
if (version != null && version < 11.0) {
|
||||
webViewController = webViewController ?? iosBelow11WebViewController;
|
||||
|
||||
if (defaultTargetPlatform == TargetPlatform.iOS || kIsWeb) {
|
||||
var shouldUseJavascript = kIsWeb;
|
||||
if (defaultTargetPlatform == TargetPlatform.iOS && !kIsWeb) {
|
||||
var platformUtil = PlatformUtil.instance();
|
||||
var version = double.tryParse(await platformUtil.getSystemVersion());
|
||||
shouldUseJavascript = version != null && version < 11.0;
|
||||
}
|
||||
if (shouldUseJavascript) {
|
||||
return await _getCookiesWithJavaScript(
|
||||
url: url, webViewController: iosBelow11WebViewController);
|
||||
url: url, webViewController: webViewController);
|
||||
}
|
||||
}
|
||||
|
||||
@ -225,10 +258,12 @@ class CookieManager {
|
||||
.toList();
|
||||
documentCookies.forEach((documentCookie) {
|
||||
List<String> cookie = documentCookie.split('=');
|
||||
cookies.add(Cookie(
|
||||
name: cookie[0],
|
||||
value: cookie[1],
|
||||
));
|
||||
if (cookie.length > 1) {
|
||||
cookies.add(Cookie(
|
||||
name: cookie[0],
|
||||
value: cookie[1],
|
||||
));
|
||||
}
|
||||
});
|
||||
return cookies;
|
||||
}
|
||||
@ -251,10 +286,12 @@ class CookieManager {
|
||||
.toList();
|
||||
documentCookies.forEach((documentCookie) {
|
||||
List<String> cookie = documentCookie.split('=');
|
||||
cookies.add(Cookie(
|
||||
name: cookie[0],
|
||||
value: cookie[1],
|
||||
));
|
||||
if (cookie.length > 1) {
|
||||
cookies.add(Cookie(
|
||||
name: cookie[0],
|
||||
value: cookie[1],
|
||||
));
|
||||
}
|
||||
});
|
||||
await headlessWebView.dispose();
|
||||
return cookies;
|
||||
@ -262,30 +299,42 @@ class CookieManager {
|
||||
|
||||
///Gets a cookie by its [name] for the given [url].
|
||||
///
|
||||
///[iosBelow11WebViewController] is used for getting the cookie (also session-only cookie) using JavaScript (cookie with `isHttpOnly` enabled cannot be found, see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies)
|
||||
///from the current context of the [WebView] managed by that controller when you need to target iOS below 11. JavaScript must be enabled in order to work.
|
||||
///[webViewController] is used for getting the cookie (also session-only cookie) using JavaScript (cookie with `isHttpOnly` enabled cannot be found, see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies)
|
||||
///from the current context of the [WebView] managed by that controller when you need to target iOS below 11 and Web platform. JavaScript must be enabled in order to work.
|
||||
///In this case the [url] parameter is ignored.
|
||||
///
|
||||
///**NOTE for iOS below 11.0**: All the cookies returned this way will have all the properties to `null` except for [Cookie.name] and [Cookie.value].
|
||||
///If [iosBelow11WebViewController] is `null` or JavaScript is disabled for it, it will try to use a [HeadlessInAppWebView]
|
||||
///If [webViewController] is `null` or JavaScript is disabled for it, it will try to use a [HeadlessInAppWebView]
|
||||
///to get the cookie (session-only cookie and cookie with `isHttpOnly` enabled won't be found!).
|
||||
///
|
||||
///**NOTE for Web**: this method will have effect only if the iframe has the same origin.
|
||||
///If [webViewController] is `null` or JavaScript is disabled for it, it will try to use a [HeadlessInAppWebView]
|
||||
///to get the cookie (session-only cookie and cookie with `isHttpOnly` enabled won't be found!).
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView
|
||||
///- iOS
|
||||
///- Web
|
||||
Future<Cookie?> getCookie(
|
||||
{required Uri url,
|
||||
required String name,
|
||||
InAppWebViewController? iosBelow11WebViewController}) async {
|
||||
@Deprecated("Use webViewController instead") InAppWebViewController? iosBelow11WebViewController,
|
||||
InAppWebViewController? webViewController}) async {
|
||||
assert(url.toString().isNotEmpty);
|
||||
assert(name.isNotEmpty);
|
||||
|
||||
if (defaultTargetPlatform == TargetPlatform.iOS) {
|
||||
var platformUtil = PlatformUtil();
|
||||
var version = double.tryParse(await platformUtil.getSystemVersion());
|
||||
if (version != null && version < 11.0) {
|
||||
webViewController = webViewController ?? iosBelow11WebViewController;
|
||||
|
||||
if (defaultTargetPlatform == TargetPlatform.iOS || kIsWeb) {
|
||||
var shouldUseJavascript = kIsWeb;
|
||||
if (defaultTargetPlatform == TargetPlatform.iOS && !kIsWeb) {
|
||||
var platformUtil = PlatformUtil.instance();
|
||||
var version = double.tryParse(await platformUtil.getSystemVersion());
|
||||
shouldUseJavascript = version != null && version < 11.0;
|
||||
}
|
||||
if (shouldUseJavascript) {
|
||||
List<Cookie> cookies = await _getCookiesWithJavaScript(
|
||||
url: url, webViewController: iosBelow11WebViewController);
|
||||
url: url, webViewController: webViewController);
|
||||
return cookies
|
||||
.cast<Cookie?>()
|
||||
.firstWhere((cookie) => cookie!.name == name, orElse: () => null);
|
||||
@ -319,31 +368,43 @@ class CookieManager {
|
||||
///The default value of [path] is `"/"`.
|
||||
///If [domain] is empty, its default value will be the domain name of [url].
|
||||
///
|
||||
///[iosBelow11WebViewController] is used for deleting the cookie (also session-only cookie) using JavaScript (cookie with `isHttpOnly` enabled cannot be deleted, see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies)
|
||||
///from the current context of the [WebView] managed by that controller when you need to target iOS below 11. JavaScript must be enabled in order to work.
|
||||
///[webViewController] is used for deleting the cookie (also session-only cookie) using JavaScript (cookie with `isHttpOnly` enabled cannot be deleted, see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies)
|
||||
///from the current context of the [WebView] managed by that controller when you need to target iOS below 11 and Web platform. JavaScript must be enabled in order to work.
|
||||
///In this case the [url] parameter is ignored.
|
||||
///
|
||||
///**NOTE for iOS below 11.0**: If [iosBelow11WebViewController] is `null` or JavaScript is disabled for it, it will try to use a [HeadlessInAppWebView]
|
||||
///**NOTE for iOS below 11.0**: If [webViewController] is `null` or JavaScript is disabled for it, it will try to use a [HeadlessInAppWebView]
|
||||
///to delete the cookie (session-only cookie and cookie with `isHttpOnly` enabled won't be deleted!).
|
||||
///
|
||||
///**NOTE for Web**: this method will have effect only if the iframe has the same origin.
|
||||
///If [webViewController] is `null` or JavaScript is disabled for it, it will try to use a [HeadlessInAppWebView]
|
||||
///to delete the cookie (session-only cookie and cookie with `isHttpOnly` enabled won't be deleted!).
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView
|
||||
///- iOS ([Official API - WKHTTPCookieStore.delete](https://developer.apple.com/documentation/webkit/wkhttpcookiestore/2882009-delete)
|
||||
///- Web
|
||||
Future<void> deleteCookie(
|
||||
{required Uri url,
|
||||
required String name,
|
||||
String domain = "",
|
||||
String path = "/",
|
||||
InAppWebViewController? iosBelow11WebViewController}) async {
|
||||
@Deprecated("Use webViewController instead") InAppWebViewController? iosBelow11WebViewController,
|
||||
InAppWebViewController? webViewController}) async {
|
||||
if (domain.isEmpty) domain = _getDomainName(url);
|
||||
|
||||
assert(url.toString().isNotEmpty);
|
||||
assert(name.isNotEmpty);
|
||||
|
||||
if (defaultTargetPlatform == TargetPlatform.iOS) {
|
||||
var platformUtil = PlatformUtil();
|
||||
var version = double.tryParse(await platformUtil.getSystemVersion());
|
||||
if (version != null && version < 11.0) {
|
||||
webViewController = webViewController ?? iosBelow11WebViewController;
|
||||
|
||||
if (defaultTargetPlatform == TargetPlatform.iOS || kIsWeb) {
|
||||
var shouldUseJavascript = kIsWeb;
|
||||
if (defaultTargetPlatform == TargetPlatform.iOS && !kIsWeb) {
|
||||
var platformUtil = PlatformUtil.instance();
|
||||
var version = double.tryParse(await platformUtil.getSystemVersion());
|
||||
shouldUseJavascript = version != null && version < 11.0;
|
||||
}
|
||||
if (shouldUseJavascript) {
|
||||
await _setCookieWithJavaScript(
|
||||
url: url,
|
||||
name: name,
|
||||
@ -351,7 +412,7 @@ class CookieManager {
|
||||
path: path,
|
||||
domain: domain,
|
||||
maxAge: -1,
|
||||
webViewController: iosBelow11WebViewController);
|
||||
webViewController: webViewController);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -369,31 +430,43 @@ class CookieManager {
|
||||
///The default value of [path] is `"/"`.
|
||||
///If [domain] is empty, its default value will be the domain name of [url].
|
||||
///
|
||||
///[iosBelow11WebViewController] is used for deleting the cookies (also session-only cookies) using JavaScript (cookies with `isHttpOnly` enabled cannot be deleted, see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies)
|
||||
///from the current context of the [WebView] managed by that controller when you need to target iOS below 11. JavaScript must be enabled in order to work.
|
||||
///[webViewController] is used for deleting the cookies (also session-only cookies) using JavaScript (cookies with `isHttpOnly` enabled cannot be deleted, see: https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies)
|
||||
///from the current context of the [WebView] managed by that controller when you need to target iOS below 11 and Web platform. JavaScript must be enabled in order to work.
|
||||
///In this case the [url] parameter is ignored.
|
||||
///
|
||||
///**NOTE for iOS below 11.0**: If [iosBelow11WebViewController] is `null` or JavaScript is disabled for it, it will try to use a [HeadlessInAppWebView]
|
||||
///**NOTE for iOS below 11.0**: If [webViewController] is `null` or JavaScript is disabled for it, it will try to use a [HeadlessInAppWebView]
|
||||
///to delete the cookies (session-only cookies and cookies with `isHttpOnly` enabled won't be deleted!).
|
||||
///
|
||||
///**NOTE for Web**: this method will have effect only if the iframe has the same origin.
|
||||
///If [webViewController] is `null` or JavaScript is disabled for it, it will try to use a [HeadlessInAppWebView]
|
||||
///to delete the cookies (session-only cookies and cookies with `isHttpOnly` enabled won't be deleted!).
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView
|
||||
///- iOS
|
||||
///- Web
|
||||
Future<void> deleteCookies(
|
||||
{required Uri url,
|
||||
String domain = "",
|
||||
String path = "/",
|
||||
InAppWebViewController? iosBelow11WebViewController}) async {
|
||||
@Deprecated("Use webViewController instead") InAppWebViewController? iosBelow11WebViewController,
|
||||
InAppWebViewController? webViewController}) async {
|
||||
if (domain.isEmpty) domain = _getDomainName(url);
|
||||
|
||||
assert(url.toString().isNotEmpty);
|
||||
|
||||
if (defaultTargetPlatform == TargetPlatform.iOS) {
|
||||
var platformUtil = PlatformUtil();
|
||||
var version = double.tryParse(await platformUtil.getSystemVersion());
|
||||
if (version != null && version < 11.0) {
|
||||
webViewController = webViewController ?? iosBelow11WebViewController;
|
||||
|
||||
if (defaultTargetPlatform == TargetPlatform.iOS || kIsWeb) {
|
||||
var shouldUseJavascript = kIsWeb;
|
||||
if (defaultTargetPlatform == TargetPlatform.iOS && !kIsWeb) {
|
||||
var platformUtil = PlatformUtil.instance();
|
||||
var version = double.tryParse(await platformUtil.getSystemVersion());
|
||||
shouldUseJavascript = version != null && version < 11.0;
|
||||
}
|
||||
if (shouldUseJavascript) {
|
||||
List<Cookie> cookies = await _getCookiesWithJavaScript(
|
||||
url: url, webViewController: iosBelow11WebViewController);
|
||||
url: url, webViewController: webViewController);
|
||||
for (var i = 0; i < cookies.length; i++) {
|
||||
await _setCookieWithJavaScript(
|
||||
url: url,
|
||||
@ -402,7 +475,7 @@ class CookieManager {
|
||||
path: path,
|
||||
domain: domain,
|
||||
maxAge: -1,
|
||||
webViewController: iosBelow11WebViewController);
|
||||
webViewController: webViewController);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -462,13 +535,15 @@ class CookieManager {
|
||||
}
|
||||
|
||||
Future<String> _getCookieExpirationDate(int expiresDate) async {
|
||||
var platformUtil = PlatformUtil();
|
||||
var platformUtil = PlatformUtil.instance();
|
||||
var dateTime = DateTime.fromMillisecondsSinceEpoch(expiresDate).toUtc();
|
||||
return await platformUtil.formatDate(
|
||||
date: dateTime,
|
||||
format: 'EEE, dd MMM yyyy hh:mm:ss z',
|
||||
locale: 'en_US',
|
||||
timezone: 'GMT');
|
||||
return !kIsWeb ?
|
||||
await platformUtil.formatDate(
|
||||
date: dateTime,
|
||||
format: 'EEE, dd MMM yyyy hh:mm:ss z',
|
||||
locale: 'en_US',
|
||||
timezone: 'GMT') :
|
||||
await platformUtil.getWebCookieExpirationDate(date: dateTime);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,10 @@ import 'package:flutter/services.dart';
|
||||
///On Android, this class has a custom implementation using `android.database.sqlite.SQLiteDatabase` because
|
||||
///[WebViewDatabase](https://developer.android.com/reference/android/webkit/WebViewDatabase)
|
||||
///doesn't offer the same functionalities as iOS `URLCredentialStorage`.
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView
|
||||
///- iOS
|
||||
class HttpAuthCredentialDatabase {
|
||||
static HttpAuthCredentialDatabase? _instance;
|
||||
static const MethodChannel _channel = const MethodChannel(
|
||||
|
@ -40,6 +40,10 @@ class InAppBrowserNotOpenedException implements Exception {
|
||||
|
||||
///This class uses the native WebView of the platform.
|
||||
///The [webViewController] field can be used to access the [InAppWebViewController] API.
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView
|
||||
///- iOS
|
||||
class InAppBrowser {
|
||||
///View ID used internally.
|
||||
late final String id;
|
||||
|
@ -7,6 +7,10 @@ import 'package:flutter/services.dart' show rootBundle;
|
||||
import 'mime_type_resolver.dart';
|
||||
|
||||
///This class allows you to create a simple server on `http://localhost:[port]/` in order to be able to load your assets file on a server. The default [port] value is `8080`.
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView
|
||||
///- iOS
|
||||
class InAppLocalhostServer {
|
||||
bool _started = false;
|
||||
HttpServer? _server;
|
||||
|
@ -18,6 +18,11 @@ import '../util.dart';
|
||||
///It can be used to run a WebView in background without attaching an `InAppWebView` to the widget tree.
|
||||
///
|
||||
///Remember to dispose it when you don't need it anymore.
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView
|
||||
///- iOS
|
||||
///- Web
|
||||
class HeadlessInAppWebView implements WebView {
|
||||
///View ID.
|
||||
late final String id;
|
||||
|
@ -20,6 +20,11 @@ import 'in_app_webview_settings.dart';
|
||||
import '../pull_to_refresh/main.dart';
|
||||
|
||||
///Flutter Widget for adding an **inline native WebView** integrated in the flutter widget tree.
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView
|
||||
///- iOS
|
||||
///- Web
|
||||
class InAppWebView extends StatefulWidget implements WebView {
|
||||
/// `gestureRecognizers` specifies which gestures should be consumed by the WebView.
|
||||
/// It is possible for other gesture recognizers to be competing with the web view on pointer
|
||||
|
@ -228,9 +228,12 @@ class InAppWebViewSettings
|
||||
|
||||
///Set to `true` to disable context menu. The default value is `false`.
|
||||
///
|
||||
///**NOTE for Web**: this setting will have effect only if the iframe has the same origin.
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView
|
||||
///- iOS
|
||||
///- Web
|
||||
bool disableContextMenu;
|
||||
|
||||
///Set to `false` if the WebView should not support zooming using its on-screen zoom controls and gestures. The default value is `true`.
|
||||
|
@ -1,10 +1,12 @@
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
///Platform native utilities
|
||||
class PlatformUtil {
|
||||
static PlatformUtil? _instance;
|
||||
static const MethodChannel _channel = const MethodChannel(
|
||||
'com.pichillilorenzo/flutter_inappwebview_platformutil');
|
||||
|
||||
///Get [PlatformUtil] instance.
|
||||
static PlatformUtil instance() {
|
||||
return (_instance != null) ? _instance! : _init();
|
||||
}
|
||||
@ -18,6 +20,8 @@ class PlatformUtil {
|
||||
static Future<dynamic> _handleMethod(MethodCall call) async {}
|
||||
|
||||
String? _cachedSystemVersion;
|
||||
|
||||
///Get current platform system version.
|
||||
Future<String> getSystemVersion() async {
|
||||
if (_cachedSystemVersion != null) {
|
||||
return _cachedSystemVersion!;
|
||||
@ -28,6 +32,7 @@ class PlatformUtil {
|
||||
return _cachedSystemVersion!;
|
||||
}
|
||||
|
||||
///Format date.
|
||||
Future<String> formatDate(
|
||||
{required DateTime date,
|
||||
required String format,
|
||||
@ -40,4 +45,12 @@ class PlatformUtil {
|
||||
args.putIfAbsent('timezone', () => timezone);
|
||||
return await _channel.invokeMethod('formatDate', args);
|
||||
}
|
||||
|
||||
///Get cookie expiration date used by Web platform.
|
||||
Future<String> getWebCookieExpirationDate(
|
||||
{required DateTime date}) async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent('date', () => date.millisecondsSinceEpoch);
|
||||
return await _channel.invokeMethod('getWebCookieExpirationDate', args);
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,10 @@ import 'pull_to_refresh_settings.dart';
|
||||
///(for example [WebView.onWebViewCreated] or [InAppBrowser.onBrowserCreated]).
|
||||
///
|
||||
///**NOTE for Android**: to be able to use the "pull-to-refresh" feature, [InAppWebViewSettings.useHybridComposition] must be `true`.
|
||||
///
|
||||
///**Supported Platforms/Implementations**:
|
||||
///- Android native WebView
|
||||
///- iOS
|
||||
class PullToRefreshController {
|
||||
@Deprecated("Use settings instead")
|
||||
// ignore: deprecated_member_use_from_same_package
|
||||
|
@ -184,6 +184,20 @@ class InAppWebViewInitialData {
|
||||
this.androidHistoryUrl = this.historyUrl;
|
||||
}
|
||||
|
||||
static InAppWebViewInitialData? fromMap(Map<String, dynamic>? map) {
|
||||
if (map == null) {
|
||||
return null;
|
||||
}
|
||||
return InAppWebViewInitialData(
|
||||
data: map["data"],
|
||||
mimeType: map["mimeType"],
|
||||
encoding: map["encoding"],
|
||||
baseUrl: map["baseUrl"],
|
||||
// ignore: deprecated_member_use_from_same_package
|
||||
androidHistoryUrl: map["androidHistoryUrl"],
|
||||
historyUrl: map["historyUrl"]);
|
||||
}
|
||||
|
||||
Map<String, String> toMap() {
|
||||
return {
|
||||
"data": data,
|
||||
|
67
lib/src/web/headless_in_app_web_view_web_element.dart
Normal file
67
lib/src/web/headless_in_app_web_view_web_element.dart
Normal file
@ -0,0 +1,67 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'in_app_web_view_web_element.dart';
|
||||
import '../util.dart';
|
||||
|
||||
class HeadlessInAppWebViewWebElement {
|
||||
String id;
|
||||
late BinaryMessenger _messenger;
|
||||
InAppWebViewWebElement? webView;
|
||||
late MethodChannel? _channel;
|
||||
|
||||
HeadlessInAppWebViewWebElement({required this.id, required BinaryMessenger messenger,
|
||||
required this.webView}) {
|
||||
this._messenger = messenger;
|
||||
|
||||
_channel = MethodChannel(
|
||||
'com.pichillilorenzo/flutter_headless_inappwebview_${this.id}',
|
||||
const StandardMethodCodec(),
|
||||
_messenger,
|
||||
);
|
||||
|
||||
this._channel?.setMethodCallHandler(handleMethodCall);
|
||||
}
|
||||
|
||||
Future<dynamic> handleMethodCall(MethodCall call) async {
|
||||
switch (call.method) {
|
||||
case "dispose":
|
||||
dispose();
|
||||
break;
|
||||
case "setSize":
|
||||
Size size = MapSize.fromMap(call.arguments['size'])!;
|
||||
setSize(size);
|
||||
break;
|
||||
case "getSize":
|
||||
return getSize().toMap();
|
||||
default:
|
||||
throw PlatformException(
|
||||
code: 'Unimplemented',
|
||||
details: 'flutter_inappwebview for web doesn\'t implement \'${call.method}\'',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void onWebViewCreated() async {
|
||||
await _channel?.invokeMethod("onWebViewCreated");
|
||||
}
|
||||
|
||||
void setSize(Size size) {
|
||||
webView?.iframe.style.width = size.width.toString() + "px";
|
||||
webView?.iframe.style.height = size.height.toString() + "px";
|
||||
}
|
||||
|
||||
Size getSize() {
|
||||
var width = webView?.iframe.getBoundingClientRect().width.toDouble() ?? 0.0;
|
||||
var height = webView?.iframe.getBoundingClientRect().height.toDouble() ?? 0.0;
|
||||
return Size(width, height);
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
_channel?.setMethodCallHandler(null);
|
||||
_channel = null;
|
||||
webView?.dispose();
|
||||
webView = null;
|
||||
}
|
||||
}
|
62
lib/src/web/headless_inappwebview_manager.dart
Normal file
62
lib/src/web/headless_inappwebview_manager.dart
Normal file
@ -0,0 +1,62 @@
|
||||
import 'dart:html';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'web_platform_manager.dart';
|
||||
import '../in_app_webview/in_app_webview_settings.dart';
|
||||
import 'in_app_web_view_web_element.dart';
|
||||
import 'headless_in_app_web_view_web_element.dart';
|
||||
|
||||
import '../types.dart';
|
||||
|
||||
class HeadlessInAppWebViewManager {
|
||||
static late MethodChannel _sharedChannel;
|
||||
|
||||
late BinaryMessenger _messenger;
|
||||
|
||||
HeadlessInAppWebViewManager({required BinaryMessenger messenger}) {
|
||||
this._messenger = messenger;
|
||||
HeadlessInAppWebViewManager._sharedChannel = MethodChannel(
|
||||
'com.pichillilorenzo/flutter_headless_inappwebview',
|
||||
const StandardMethodCodec(),
|
||||
_messenger,
|
||||
);
|
||||
HeadlessInAppWebViewManager._sharedChannel.setMethodCallHandler(handleMethod);
|
||||
}
|
||||
|
||||
Future<dynamic> handleMethod(MethodCall call) async {
|
||||
switch (call.method) {
|
||||
case "run":
|
||||
String id = call.arguments["id"];
|
||||
Map<String, dynamic> params = call.arguments["params"].cast<String, dynamic>();
|
||||
run(id, params);
|
||||
break;
|
||||
default:
|
||||
throw UnimplementedError("Unimplemented ${call.method} method");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void run(String id, Map<String, dynamic> params) {
|
||||
var webView = InAppWebViewWebElement(viewId: id, messenger: _messenger);
|
||||
var headlessWebView = HeadlessInAppWebViewWebElement(
|
||||
id: id,
|
||||
messenger: _messenger,
|
||||
webView: webView
|
||||
);
|
||||
WebPlatformManager.webViews.putIfAbsent(id, () => webView);
|
||||
webView.iframe.style.display = 'none';
|
||||
Map<String, dynamic> initialSettings = params["initialSettings"].cast<String, dynamic>();
|
||||
if (initialSettings.isEmpty) {
|
||||
webView.initialSettings = InAppWebViewSettings();
|
||||
} else {
|
||||
webView.initialSettings = InAppWebViewSettings.fromMap(initialSettings);
|
||||
}
|
||||
webView.initialUrlRequest = URLRequest.fromMap(params["initialUrlRequest"]?.cast<String, dynamic>());
|
||||
webView.initialFile = params["initialFile"];
|
||||
webView.initialData = InAppWebViewInitialData.fromMap(params["initialData"]?.cast<String, dynamic>());
|
||||
document.body?.append(webView.iframe);
|
||||
webView.prepare();
|
||||
headlessWebView.onWebViewCreated();
|
||||
webView.makeInitialLoad();
|
||||
}
|
||||
}
|
@ -3,14 +3,15 @@ import 'package:flutter/services.dart';
|
||||
import 'dart:html';
|
||||
import 'dart:js' as js;
|
||||
|
||||
import 'web_platform_manager.dart';
|
||||
import '../in_app_webview/in_app_webview_settings.dart';
|
||||
import '../types.dart';
|
||||
|
||||
class InAppWebViewWebElement {
|
||||
late int _viewId;
|
||||
late dynamic _viewId;
|
||||
late BinaryMessenger _messenger;
|
||||
late IFrameElement iframe;
|
||||
late MethodChannel _channel;
|
||||
late MethodChannel? _channel;
|
||||
InAppWebViewSettings? initialSettings;
|
||||
URLRequest? initialUrlRequest;
|
||||
InAppWebViewInitialData? initialData;
|
||||
@ -20,7 +21,7 @@ class InAppWebViewWebElement {
|
||||
late js.JsObject bridgeJsObject;
|
||||
bool isLoading = false;
|
||||
|
||||
InAppWebViewWebElement({required int viewId, required BinaryMessenger messenger}) {
|
||||
InAppWebViewWebElement({required dynamic viewId, required BinaryMessenger messenger}) {
|
||||
this._viewId = viewId;
|
||||
this._messenger = messenger;
|
||||
iframe = IFrameElement()
|
||||
@ -35,11 +36,10 @@ class InAppWebViewWebElement {
|
||||
_messenger,
|
||||
);
|
||||
|
||||
this._channel.setMethodCallHandler(handleMethodCall);
|
||||
this._channel?.setMethodCallHandler(handleMethodCall);
|
||||
|
||||
bridgeJsObject = js.JsObject.fromBrowserObject(js.context['flutter_inappwebview']);
|
||||
bridgeJsObject['viewId'] = _viewId;
|
||||
bridgeJsObject['iframeId'] = iframe.id;
|
||||
bridgeJsObject = js.JsObject.fromBrowserObject(js.context[WebPlatformManager.BRIDGE_JS_OBJECT_NAME]);
|
||||
bridgeJsObject['webViews'][_viewId] = bridgeJsObject.callMethod("createFlutterInAppWebView", [_viewId, iframe.id]);
|
||||
}
|
||||
|
||||
/// Handles method calls over the MethodChannel of this plugin.
|
||||
@ -87,6 +87,9 @@ class InAppWebViewWebElement {
|
||||
InAppWebViewSettings newSettings = InAppWebViewSettings.fromMap(call.arguments["settings"].cast<String, dynamic>());
|
||||
setSettings(newSettings);
|
||||
break;
|
||||
case "dispose":
|
||||
dispose();
|
||||
break;
|
||||
default:
|
||||
throw PlatformException(
|
||||
code: 'Unimplemented',
|
||||
@ -118,7 +121,16 @@ class InAppWebViewWebElement {
|
||||
iframe.setAttribute("sandbox", sandbox.map((e) => e.toValue()).join(" "));
|
||||
}
|
||||
|
||||
bridgeJsObject.callMethod("prepare", [js.JsObject.jsify(settings.toMap())]);
|
||||
_callMethod("prepare", [js.JsObject.jsify(settings.toMap())]);
|
||||
}
|
||||
|
||||
dynamic _callMethod(Object method, [List? args]) {
|
||||
var webViews = bridgeJsObject['webViews'] as js.JsObject;
|
||||
if (webViews.hasProperty(_viewId)) {
|
||||
var webview = bridgeJsObject['webViews'][_viewId] as js.JsObject;
|
||||
return webview.callMethod(method, args);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void makeInitialLoad() async {
|
||||
@ -168,27 +180,27 @@ class InAppWebViewWebElement {
|
||||
}
|
||||
|
||||
Future<void> reload() async {
|
||||
bridgeJsObject.callMethod("reload");
|
||||
_callMethod("reload");
|
||||
}
|
||||
|
||||
Future<void> goBack() async {
|
||||
bridgeJsObject.callMethod("goBack");
|
||||
_callMethod("goBack");
|
||||
}
|
||||
|
||||
Future<void> goForward() async {
|
||||
bridgeJsObject.callMethod("goForward");
|
||||
_callMethod("goForward");
|
||||
}
|
||||
|
||||
Future<void> goBackOrForward({required int steps}) async {
|
||||
bridgeJsObject.callMethod("goBackOrForward", [steps]);
|
||||
_callMethod("goBackOrForward", [steps]);
|
||||
}
|
||||
|
||||
Future<dynamic> evaluateJavascript({required String source}) async {
|
||||
return bridgeJsObject.callMethod("evaluateJavascript", [source]);
|
||||
return _callMethod("evaluateJavascript", [source]);
|
||||
}
|
||||
|
||||
Future<void> stopLoading() async {
|
||||
bridgeJsObject.callMethod("stopLoading");
|
||||
_callMethod("stopLoading");
|
||||
}
|
||||
|
||||
Set<Sandbox> getSandbox() {
|
||||
@ -243,7 +255,7 @@ class InAppWebViewWebElement {
|
||||
iframe.setAttribute("sandbox", sandbox.map((e) => e.toValue()).join(" "));
|
||||
}
|
||||
|
||||
bridgeJsObject.callMethod("setSettings", [js.JsObject.jsify(newSettings.toMap())]);
|
||||
_callMethod("setSettings", [js.JsObject.jsify(newSettings.toMap())]);
|
||||
|
||||
settings = newSettings;
|
||||
}
|
||||
@ -254,7 +266,7 @@ class InAppWebViewWebElement {
|
||||
var obj = {
|
||||
"url": url
|
||||
};
|
||||
await _channel.invokeMethod("onLoadStart", obj);
|
||||
await _channel?.invokeMethod("onLoadStart", obj);
|
||||
}
|
||||
|
||||
void onLoadStop(String url) async {
|
||||
@ -263,14 +275,14 @@ class InAppWebViewWebElement {
|
||||
var obj = {
|
||||
"url": url
|
||||
};
|
||||
await _channel.invokeMethod("onLoadStop", obj);
|
||||
await _channel?.invokeMethod("onLoadStop", obj);
|
||||
}
|
||||
|
||||
void onUpdateVisitedHistory(String url) async {
|
||||
var obj = {
|
||||
"url": url
|
||||
};
|
||||
await _channel.invokeMethod("onUpdateVisitedHistory", obj);
|
||||
await _channel?.invokeMethod("onUpdateVisitedHistory", obj);
|
||||
}
|
||||
|
||||
void onScrollChanged(int x, int y) async {
|
||||
@ -278,7 +290,7 @@ class InAppWebViewWebElement {
|
||||
"x": x,
|
||||
"y": y
|
||||
};
|
||||
await _channel.invokeMethod("onScrollChanged", obj);
|
||||
await _channel?.invokeMethod("onScrollChanged", obj);
|
||||
}
|
||||
|
||||
void onConsoleMessage(String type, String? message) async {
|
||||
@ -302,7 +314,7 @@ class InAppWebViewWebElement {
|
||||
"messageLevel": messageLevel,
|
||||
"message": message
|
||||
};
|
||||
await _channel.invokeMethod("onConsoleMessage", obj);
|
||||
await _channel?.invokeMethod("onConsoleMessage", obj);
|
||||
}
|
||||
|
||||
Future<bool?> onCreateWindow(int windowId, String url, String? target, String? windowFeatures) async {
|
||||
@ -330,15 +342,15 @@ class InAppWebViewWebElement {
|
||||
},
|
||||
"windowFeatures": windowFeaturesMap
|
||||
};
|
||||
return await _channel.invokeMethod("onCreateWindow", obj);
|
||||
return await _channel?.invokeMethod("onCreateWindow", obj);
|
||||
}
|
||||
|
||||
void onWindowFocus() async {
|
||||
await _channel.invokeMethod("onWindowFocus");
|
||||
await _channel?.invokeMethod("onWindowFocus");
|
||||
}
|
||||
|
||||
void onWindowBlur() async {
|
||||
await _channel.invokeMethod("onWindowBlur");
|
||||
await _channel?.invokeMethod("onWindowBlur");
|
||||
}
|
||||
|
||||
void onPrint(String? url) async {
|
||||
@ -346,15 +358,15 @@ class InAppWebViewWebElement {
|
||||
"url": url
|
||||
};
|
||||
|
||||
await _channel.invokeMethod("onPrint", obj);
|
||||
await _channel?.invokeMethod("onPrint", obj);
|
||||
}
|
||||
|
||||
void onEnterFullscreen() async {
|
||||
await _channel.invokeMethod("onEnterFullscreen");
|
||||
await _channel?.invokeMethod("onEnterFullscreen");
|
||||
}
|
||||
|
||||
void onExitFullscreen() async {
|
||||
await _channel.invokeMethod("onExitFullscreen");
|
||||
await _channel?.invokeMethod("onExitFullscreen");
|
||||
}
|
||||
|
||||
void onTitleChanged(String? title) async {
|
||||
@ -362,7 +374,7 @@ class InAppWebViewWebElement {
|
||||
"title": title
|
||||
};
|
||||
|
||||
await _channel.invokeMethod("onTitleChanged", obj);
|
||||
await _channel?.invokeMethod("onTitleChanged", obj);
|
||||
}
|
||||
|
||||
void onZoomScaleChanged(double oldScale, double newScale) async {
|
||||
@ -371,6 +383,20 @@ class InAppWebViewWebElement {
|
||||
"newScale": newScale
|
||||
};
|
||||
|
||||
await _channel.invokeMethod("onZoomScaleChanged", obj);
|
||||
await _channel?.invokeMethod("onZoomScaleChanged", obj);
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
_channel?.setMethodCallHandler(null);
|
||||
_channel = null;
|
||||
iframe.remove();
|
||||
if (WebPlatformManager.webViews.containsKey(_viewId)) {
|
||||
WebPlatformManager.webViews.remove(_viewId);
|
||||
}
|
||||
bridgeJsObject = js.JsObject.fromBrowserObject(js.context[WebPlatformManager.BRIDGE_JS_OBJECT_NAME]);
|
||||
var webViews = bridgeJsObject['webViews'] as js.JsObject;
|
||||
if (webViews.hasProperty(_viewId)) {
|
||||
webViews.deleteProperty(_viewId);
|
||||
}
|
||||
}
|
||||
}
|
46
lib/src/web/platform_util.dart
Normal file
46
lib/src/web/platform_util.dart
Normal file
@ -0,0 +1,46 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'dart:js' as js;
|
||||
|
||||
import 'web_platform_manager.dart';
|
||||
|
||||
class PlatformUtil {
|
||||
late BinaryMessenger _messenger;
|
||||
late MethodChannel? _channel;
|
||||
|
||||
PlatformUtil({required BinaryMessenger messenger}) {
|
||||
this._messenger = messenger;
|
||||
|
||||
_channel = MethodChannel(
|
||||
'com.pichillilorenzo/flutter_inappwebview_platformutil',
|
||||
const StandardMethodCodec(),
|
||||
_messenger,
|
||||
);
|
||||
|
||||
this._channel?.setMethodCallHandler(handleMethodCall);
|
||||
}
|
||||
|
||||
Future<dynamic> handleMethodCall(MethodCall call) async {
|
||||
switch (call.method) {
|
||||
case "getWebCookieExpirationDate":
|
||||
int timestamp = call.arguments['date'];
|
||||
return getWebCookieExpirationDate(timestamp);
|
||||
default:
|
||||
throw PlatformException(
|
||||
code: 'Unimplemented',
|
||||
details: 'flutter_inappwebview for web doesn\'t implement \'${call.method}\'',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String getWebCookieExpirationDate(int timestamp) {
|
||||
var bridgeJsObject = js.JsObject.fromBrowserObject(js.context[WebPlatformManager.BRIDGE_JS_OBJECT_NAME]);
|
||||
return bridgeJsObject.callMethod("getCookieExpirationDate", [timestamp]);
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
_channel?.setMethodCallHandler(null);
|
||||
_channel = null;
|
||||
}
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'headless_inappwebview_manager.dart';
|
||||
import 'web_platform_manager.dart';
|
||||
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
|
||||
import 'shims/dart_ui.dart' as ui;
|
||||
|
||||
import 'in_app_web_view_web_element.dart';
|
||||
|
||||
import 'platform_util.dart';
|
||||
import 'package:js/js.dart';
|
||||
|
||||
/// Builds an iframe based WebView.
|
||||
@ -26,30 +26,21 @@ class FlutterInAppWebViewWebPlatform {
|
||||
|
||||
static void registerWith(Registrar registrar) {
|
||||
final pluginInstance = FlutterInAppWebViewWebPlatform(registrar);
|
||||
final platformUtil = PlatformUtil(messenger: registrar);
|
||||
final headlessManager = HeadlessInAppWebViewManager(messenger: registrar);
|
||||
_nativeCommunication = allowInterop(_dartNativeCommunication);
|
||||
}
|
||||
|
||||
/// Handles method calls over the MethodChannel of this plugin.
|
||||
Future<dynamic> handleMethodCall(MethodCall call) async {
|
||||
switch (call.method) {
|
||||
default:
|
||||
throw PlatformException(
|
||||
code: 'Unimplemented',
|
||||
details: 'flutter_inappwebview for web doesn\'t implement \'${call.method}\'',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Allows assigning a function to be callable from `window.flutter_inappwebview.nativeCommunication()`
|
||||
@JS('flutter_inappwebview.nativeCommunication')
|
||||
external set _nativeCommunication(Future<dynamic> Function(String method, int viewId, [List? args]) f);
|
||||
external set _nativeCommunication(Future<dynamic> Function(String method, dynamic viewId, [List? args]) f);
|
||||
|
||||
/// Allows calling the assigned function from Dart as well.
|
||||
@JS()
|
||||
external Future<dynamic> nativeCommunication(String method, int viewId, [List? args]);
|
||||
external Future<dynamic> nativeCommunication(String method, dynamic viewId, [List? args]);
|
||||
|
||||
Future<dynamic> _dartNativeCommunication(String method, int viewId, [List? args]) async {
|
||||
Future<dynamic> _dartNativeCommunication(String method, dynamic viewId, [List? args]) async {
|
||||
if (WebPlatformManager.webViews.containsKey(viewId)) {
|
||||
var webViewHtmlElement = WebPlatformManager.webViews[viewId] as InAppWebViewWebElement;
|
||||
switch (method) {
|
||||
|
@ -1,3 +1,4 @@
|
||||
abstract class WebPlatformManager {
|
||||
static final Map<int, dynamic> webViews = {};
|
||||
static final String BRIDGE_JS_OBJECT_NAME = "flutter_inappwebview";
|
||||
static final Map<dynamic, dynamic> webViews = {};
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user