webviewx
webviewx copied to clipboard
navigationDelegate in web
navigationDelegate method don work in web render app.
same issue
In my case i run into this issue, but i need to watch Navigation History. What i found is that the navigation delegate only triggers when user interacts, ex a touch event, it may works, but in my particular case, the navigation history is something that i cannot watch with the delegate :(
Hello, @mahanabd @artem312 @aperez-kueski, this is actually noted here, precisely:
NavigationDelegate only has effect when using SourceType.urlBypass because click handlers can only be registered for this type of source (for now, soon they may also work when using SourceType.html - see src/view/impl/web.dart around line 220)
I've been playing with the example app and the navigationDelegate works perfectly fine when using SourceType.urlBypass
.
I guess I could eventually make it also work for SourceType.html
too, but that's pretty much it. There's no way to make it work with SourceType.url
, at least not as far as I know.
Also, @aperez-kueski, you're right. The navigation delegate only triggers on user interaction, such as when clicking a link. This is intended behaviour because this is how it works on mobile, too. I may or may not be able to make it work for all history change events (such as changing the url from the controller, etc), but even if I do it would then behave differently compared to the mobile version. That being said, this feature is not really something I'm willing to add anytime soon. You can still fork the package and implement it yourself, if that's something you need for your use-case.
@adrianflutur i am using SourceType.html, how can I make NavigationDelegate work on WEB?
@kekko7072 You can't right now
@kekko7072 we have just implemented a workaround that may work for you. Caveat: we control the HTML content loaded into our WebViewX widget. We need to control how anchors behave - launching some urls and some are intercepted and loaded within the app itself.
Ideally we would simply utilise the NavigationDelegate, but that doesn't work for web at the moment. To get around this we implement a piece of javascript code that tells the browser not to respond to the click event of anchors. We insert this script into the body of the HTML content. This is as follows:
<script>
//listen for link click events at the document level
if (document.addEventListener) {
document.addEventListener('click', interceptClickEvent);
} else if (document.attachEvent) {
document.attachEvent('onclick', interceptClickEvent);
}
function interceptClickEvent(e) {
var href;
var target = e.target || e.srcElement;
if (target.tagName === 'A') {
href = target.getAttribute('href');
//tell the browser not to respond to the link click
e.preventDefault();
sendNavigationEvent(href);
}
}
</script>
Then we add some jsContent and a dartCallBack to the WebViewX widget. As follows:
jsContent: const {
EmbeddedJsContent(
webJs:
"function sendNavigationEvent(url) { NavigationCallback(url) }",
mobileJs:
"function sendNavigationEvent(url) { NavigationCallback.postMessage(url) }",
),
},
dartCallBacks: {
DartCallback(
name: 'NavigationCallback',
callBack: (url) => _interceptNavigation(url),
)
},
This ensures that the browser does not immediately respond to an anchor being clicked and instead passes the URL back to the dart function _interceptNavigation(url). This function looks as follows:
void _interceptNavigation(String url) {
Uri uri = Uri.parse(url);
if (_deepLinksService.isDeepLink(uri)) {
// This is an internal link, so get the deeplinking service to handle it
_deepLinksService.navigateToDeepLink(uri, context);
} else {
_launchURL(url);
}
}
We parse the Url and determine if this is content that we should be loading within the app. If it is something that the app should handle, it does so. If not, it launches the Url using the web, iOS or Android default method.
I'm not sure if this helps at all, but it ensures we have consistent cross platform web, iOS and Android behaviour.
Lastly, kudos to @adrianflutur - nice work on the most fully featured package that works cross platform. Great work.
@alexanderkenney so I can enable it using an upgrade? Thanks
@kekko7072 no. Its not an upgrade. It's a workaround that we use to ensure we get consistent behaviour across iOS, Android and web. Take a look at the code above to see how we have implemented it.
Essentially it makes use of the EmbeddedJsContent and dartCallback options available to webviewx.
@kekko7072 no. Its not an upgrade. It's a workaround that we use to ensure we get consistent behaviour across iOS, Android and web. Take a look at the code above to see how we have implemented it.
Essentially it makes use of the EmbeddedJsContent and dartCallback options available to webviewx.
ok perfect
sendNavigationEvent
Hello @alexanderkenney, I'm trying this approach but I'm getting the next error:
Uncaught (in promise) ReferenceError: onSuccessAuthEvent is not defined
at onSuccessAuth ((index):1:600)
at e.C [as callback] (web3auth.tsx:152:12)
at get_user_data_use_case.ts:31:22
at b (regeneratorRuntime.js:86:17)
at Generator._invoke (regeneratorRuntime.js:66:24)
at Generator.next (regeneratorRuntime.js:117:21)
at a (asyncToGenerator.js:3:20)
at f (asyncToGenerator.js:25:9)
My code: index.html
<body>
<script type="text/javascript">
function onSuccessAuth(data){
onSuccessAuthEvent(data);
}
function onErrorAuth(error){
onErrorAuthEvent(error);
}
</script>
<div id="root"></div>
</body>
</html>
Flutter web:
return WebViewX(
initialContent: url,
height: screenSize.height,
width: screenSize.width,
javascriptMode: JavascriptMode.unrestricted,
jsContent: const {
EmbeddedJsContent(
js: "function onSuccessAuthEvent(data) { OnSuccessAuthCallback(data) }",
),
},
dartCallBacks: {
DartCallback(
name: 'OnSuccessAuthCallback',
callBack: (data) => _onSuccessAuth(data),
)
},
);
Any idea about what is wrong here?
sendNavigationEvent
Hello @alexanderkenney, I'm trying this approach but I'm getting the next error:
Uncaught (in promise) ReferenceError: onSuccessAuthEvent is not defined at onSuccessAuth ((index):1:600) at e.C [as callback] (web3auth.tsx:152:12) at get_user_data_use_case.ts:31:22 at b (regeneratorRuntime.js:86:17) at Generator._invoke (regeneratorRuntime.js:66:24) at Generator.next (regeneratorRuntime.js:117:21) at a (asyncToGenerator.js:3:20) at f (asyncToGenerator.js:25:9)
My code: index.html
<body> <script type="text/javascript"> function onSuccessAuth(data){ onSuccessAuthEvent(data); } function onErrorAuth(error){ onErrorAuthEvent(error); } </script> <div id="root"></div> </body> </html>
Flutter web:
return WebViewX( initialContent: url, height: screenSize.height, width: screenSize.width, javascriptMode: JavascriptMode.unrestricted, jsContent: const { EmbeddedJsContent( js: "function onSuccessAuthEvent(data) { OnSuccessAuthCallback(data) }", ), }, dartCallBacks: { DartCallback( name: 'OnSuccessAuthCallback', callBack: (data) => _onSuccessAuth(data), ) }, );
Any idea about what is wrong here?
I found the issue, looks like webviewx doesn't support jsContent with SourceType.url when source != 'about:blank'.
Calling any method that will interact with the page's source (i.e. call JS methods, callbacks, execute JS etc) from the controller only work when using SourceType.urlBypass or SourceType.html. This is because only in theese 2 situations the package can actually inject that JS code inside the page source. When using SourceType.url, iframe's content does not actually belong to us - it is loaded from a different domain so we're not allowed to do that.
https://github.com/adrianflutur/webviewx/blob/e10be277c2b73c6177a0ce97dab079a8c5a9fe35/lib/src/view/impl/web.dart#L418-L455