GithubHelp home page GithubHelp logo

adrianflutur / webviewx Goto Github PK

View Code? Open in Web Editor NEW
84.0 84.0 117.0 4.47 MB

A feature-rich cross-platform webview using webview_flutter for mobile and iframe for web. JS interop-ready.

License: MIT License

Kotlin 0.13% HTML 1.54% Swift 0.38% Objective-C 0.04% Dart 97.91%

webviewx's People

Contributors

adrianflutur avatar azzeccagarbugli avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

webviewx's Issues

Error: NoSuchMethodError: 'pay' method not found

Im trying to build a webview which have a dynamic script and call a function of that script whent the page is loaded. But I'm getting no such method error on callJsMethod even though I have created the method in jsContent.

This is the log

Error: NoSuchMethodError: 'pay'
method not found
Receiver: Instance of 'JavaScriptObject'
Arguments: []
    at Object.throw_ [as throw] (http://localhost:56359/dart_sdk.js:5061:11)
    at js.JsObject._fromJs.callMethod (http://localhost:56359/dart_sdk.js:58823:19)
    at web.WebViewXController.new.callJsMethod (http://localhost:56359/packages/webviewx/src/controller/impl/web.dart.lib.js:111:35)
    at http://localhost:56359/packages/pasar_negeri/screens/transactions/transactions_screen.dart.lib.js:3938:42
    at Generator.next (<anonymous>)
    at runBody (http://localhost:56359/dart_sdk.js:38659:34)
    at Object._async [as async] (http://localhost:56359/dart_sdk.js:38690:7)
    at http://localhost:56359/packages/pasar_negeri/screens/transactions/transactions_screen.dart.lib.js:3937:151
    at _RootZone.runUnary (http://localhost:56359/dart_sdk.js:38511:59)
    at _FutureListener.then.handleValue (http://localhost:56359/dart_sdk.js:33713:29)
    at handleValueCallback (http://localhost:56359/dart_sdk.js:34265:49)
    at Function._propagateToListeners (http://localhost:56359/dart_sdk.js:34303:17)
    at async._AsyncCallbackEntry.new.callback (http://localhost:56359/dart_sdk.js:34032:27)
    at Object._microtaskLoop (http://localhost:56359/dart_sdk.js:38778:13)
    at _startMicrotaskLoop (http://localhost:56359/dart_sdk.js:38784:13)
    at http://localhost:56359/dart_sdk.js:34519:9

This is the page source code

import 'dart:convert';
import 'dart:io' show Platform;

import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:flutter/services.dart';
import 'package:webviewx/webviewx.dart';

import '../../config/config.dart';
import '../../helpers/utils.dart';
import '../../models/checkout.dart';
import '../../models/midtrans.dart';
import '../../screens/authentication/authentication_wrapper.dart';
import '../../screens/transactions/transaction_details_screen.dart';

class SnapScreen extends StatefulWidget {
  static const routeName = '/SNAP';

  const SnapScreen({Key? key}) : super(key: key);

  @override
  _SnapScreenState createState() => _SnapScreenState();
}

class _SnapScreenState extends State<SnapScreen> {
  @override
  void initState() {
    super.initState();
  }

  @override
  void didUpdateWidget(SnapScreen oldWidget) {
    super.didUpdateWidget(oldWidget);
  }

  @override
  void dispose() {
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final Checkout paymentInfo =
        ModalRoute.of(context)?.settings.arguments as Checkout;

    final mediaQuery = MediaQuery.of(context);

    return Scaffold(
      appBar: AppBar(
        title: Text(
          'PEMBAYARAN',
        ),
        elevation: 2,
      ),
      body: Stack(
        children: <Widget>[
          WebViewX(
            height: mediaQuery.size.height - mediaQuery.padding.vertical,
            width: mediaQuery.size.width - mediaQuery.padding.horizontal,
            navigationDelegate: (navigation) async {
              if (await canLaunch(navigation.content.source)) {
                await launch(navigation.content.source);
              }

              return NavigationDecision.prevent;
            },
            initialContent: '<html></html>',
            initialSourceType: SourceType.html,
            javascriptMode: JavascriptMode.unrestricted,
            jsContent: {
              EmbeddedJsContent(
                mobileJs: """
                function pay(transactionToken, midtransUrl, midtransClientKey) {
                  var config = document.createElement('script');
                  config.type = 'text/javascript';
                  config.src = midtransUrl;
                  config.setAttribute('data-client-key', midtransClientKey);
                  config.onload = function () {
                    snap.pay(transactionToken, {
                      // Optional
                      onSuccess: function (result) {
                        Android.postMessage('ok');
                        Print.postMessage(result);
                      },
                      // Optional
                      onPending: function (result) {
                        Android.postMessage('pending');
                        Print.postMessage(result);
                      },
                      // Optional
                      onError: function (result) {
                        Android.postMessage('error');
                        Print.postMessage(result);
                      },
                      onClose: function () {
                        Android.postMessage('close');
                        Print.postMessage('close');
                      }
                    });
                  }
            
                  document.head.append(config);
                }
                """,
                webJs: """
                function pay(transactionToken, midtransUrl, midtransClientKey) {
                  var config = document.createElement('script');
                  config.type = 'text/javascript';
                  config.src = midtransUrl;
                  config.setAttribute('data-client-key', midtransClientKey);
                  config.onload = function () {
                    snap.pay(transactionToken, {
                      // Optional
                      onSuccess: function (result) {
                        Android('ok');
                        Print(result);
                      },
                      // Optional
                      onPending: function (result) {
                        Android('pending');
                        Print(result);
                      },
                      // Optional
                      onError: function (result) {
                        Android('error');
                        Print(result);
                      },
                      onClose: function () {
                        Android('close');
                        Print('close');
                      }
                    });
                  }
            
                  document.head.append(config);
                }
                """,
              )
              /*EmbeddedJsContent(
                webJs: "function callPay() { pay('${paymentInfo.token}','${Config.midtransURL}','${Config.midtransClientKey}') }",
                mobileJs: "function callPay() { pay('${paymentInfo.token}','${Config.midtransURL}','${Config.midtransClientKey}') }",
              ),*/
            },
            dartCallBacks: {
              DartCallback(
                name: 'Print',
                callBack: (receiver) {
                  print('==========>>>>>>>>>>>>>> BEGIN');
                  print('Print Message : ' + receiver.message);
                  print(receiver.message);
                  if (receiver.message != 'undefined') {
                    _handleResponse(receiver.message, paymentInfo);
                  }
                  print('==========>>>>>>>>>>>>>> END');
                },
              ),
              DartCallback(
                name: 'Android',
                callBack: (receiver) {
                  print('==========>>>>>>>>>>>>>> BEGIN');
                  print('Android Message : ' + receiver.message);
                  if (Platform.isAndroid) {
                    if (receiver.message != 'undefined') {
                      if (receiver.message == 'close') {
                        Navigator.pop(context);
                      } else {
                        _handleResponse(receiver.message, paymentInfo);
                      }
                    }
                  }
                  print('==========>>>>>>>>>>>>>> END');
                },
              ),
            },
            onWebViewCreated: (_controller) {
              print('LOADING HTML WITH SNAP TOKEN : ' + paymentInfo.token);
              _loadHtmlFromAssets(_controller, paymentInfo.token);
              //await _loadHtmlFromAssets(_controller);
            },
          ),
        ],
      ),
    );
  }

  _loadHtmlFromAssets(
    WebViewXController webViewController, transactionToken
  ) async {
    webViewController
        .loadContent('assets/html/snap.html', SourceType.html, fromAssets: true)
        .then((_) async => await webViewController.callJsMethod("pay", [transactionToken, Config.midtransURL, Config.midtransClientKey]));
  }

  _handleResponse(message, paymentInfo) {
    var title, desc;
    Midtrans? midtrans;

    Navigator.pop(context);

    if (Platform.isAndroid) {
      switch (message) {
        case 'ok':
          midtrans = Midtrans(MIDTRANS_PAYMENT_TYPE.bank_transfer,
              MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_200);
          break;
        case 'pending':
          midtrans = Midtrans(MIDTRANS_PAYMENT_TYPE.bank_transfer,
              MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_201);
          break;
        case 'error':
          midtrans = Midtrans(MIDTRANS_PAYMENT_TYPE.bank_transfer,
              MIDTRANS_STATUS_CODE.MIDTRANS_STATUS_CODE_202);
          break;
      }
    } else {
      midtrans = Midtrans.fromString(message);
    }
    var result = midtrans?.getResult();
    title = result?[0];
    desc = result?[1];
    if (title.length == null && desc.length == null)
      utils.toast('Something went wrong!');
    else
      _showConfirmDialog(title, desc, paymentInfo);
  }

  void _showConfirmDialog(title, desc, paymentInfo) async {
    await showModalBottomSheet(
      context: context,
      isScrollControlled: true,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
      ),
      builder: (BuildContext context) {
        return Container(
          height: 250,
          child: Padding(
            padding: const EdgeInsets.all(10),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.center,
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Text(
                  title,
                  style: AppConfig.theme?.styles.title3,
                ),
                SizedBox(
                  height: 25,
                ),
                Text(
                  desc,
                  style: AppConfig.theme?.styles.body1,
                ),
                SizedBox(
                  height: 25,
                ),
                TextButton(
                  onPressed: () async {
                    Navigator.popUntil(context, (route) {
                      return route.settings.name ==
                          AuthenticationWrapper.routeName;
                    });
                    Navigator.pushNamed(
                        context, TransactionDetailsScreen.routeName,
                        arguments: paymentInfo.orderId);
                  },
                  child: Text('Lihat Detail Pembayaran'),
                )
              ],
            ),
          ),
        );
      },
    );
  }
}

This is the snap.html

<html>

<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <script type="text/javascript">
  </script>
</head>

<body>
</body>

</html>

Connection refused on initial url

hello,
I am building a web app with flutter and I need to have a user log in. I am doing this by hitting a authentication endpoint on an azure AD so I can get a code and subsequently get a token. here it what the initial URL looks like. line breaks added for readability. Also anything in <> is info that I would rather keep private.

https://login.microsoftonline.com/<tenant_id>/oauth2/v2.0/authorize?
client_id=<client_id>
&redirect_uri=$redirectUrl
&response_type=code
&response_mode=query
&scope=https://graph.microsoft.com/user.read
&code_challenge=<code_challenge>
&code_challenge_method=<code_challenge_method>

obviously this isn't of much help to anyone but i just wanted to give a sense of the structed or the URL. but if you would like to do you own test you can just use the link below which has the same result.
https://login.microsoftonline.com/

my webapp is hosted on firebase but also works on local host. however neither firebase or an azure ad are necessary to to reproduce the bug as you would just need a microsoft account and can use https://login.microsoftonline.com/ as the URL in WebViewX and make sure the flutter project has web.
Any clue as to how to get this working? any help would be greatly appreciated.

as for the set up of me WebViewx refer to code below

`@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("o365 login"),
actions: [
Padding(
padding: EdgeInsets.only(right: 20),
child: GestureDetector(
child: Icon(Icons.home),
onTap: () => _setUrl(), //I also has a method setup to set the same url as above to see if it was the initial URL causing the problem
),
)
],

  ),
  //Builder is wrapped around the webview to allow SnackBar to access the build context of the scaffold.
  // Without this the context would be from AuthLogin which does not contain a scaffold.
  body: Builder(builder: (context) =>
      WebViewX(
        key: ValueKey('webviewx'),
        initialContent: _initialUrl_noredirect,
        initialSourceType: SourceType.URL_BYPASS,
        onWebViewCreated: (controller) => webviewController = controller,
        onPageStarted: (url) => print('A new page has started loading...\n'),
        // onPageFinished: _getAuthCode,
        javascriptMode: JavascriptMode.unrestricted,
        webSpecificParams: WebSpecificParams(additionalSandboxOptions: ['allow-top-navigation', 'allow-top-navigation-by-user-activation', 'allow-downloads',
          'allow-popups',
          'allow-popups-to-escape-sandbox',
          'allow-presentation',
          'allow-same-origin',])
      )
  ),
);

}
`

Javascript Bridge Problems on the Web

// flutter
WebViewXController _wc = WebViewXController();
...
dartCallBacks: Set.from([
DartCallback(
name: "test",
callBack: (message) {
showSnackBar('${message.toString()}', context);
},
),
]),

void _evalRawJsInGlobalContext() async {
try {
var result = await _wc.evalRawJavascript(
"jstestFunction('string')",
);
} catch (e) {
showAlertDialog("${e}", context);
}
}

// html

<script> function jstestFunction(text){ console.log(text); if (window.test && window.test.postMessage) { console.log(document.documentElement.innerHTML); test.postMessage(text); test(text); } } </script>

When running on mobile, the value delivered is displayed normally, but when running on the web, the value is generated as null. How do I fix this?
Or is it not supported on the web?

web Error message : nosuchmethoderror : nvalid member on null : 'callMethod'

Open paypal execute URL in flutter web.

How to get payment callback in flutter web.

This is my code:

WebViewX(
width: 1500,
height: 600,
initialContent: " https://api.sandbox.paypal.com/v1/payments/payment/PAYID-ABCCCCSCSCSCSCSC/execute",
javascriptMode: JavascriptMode.unrestricted,
initialSourceType: SourceType.url,
onWebViewCreated: (controller) => webviewController = controller,
// ignoreAllGestures: false,
onWebResourceError: (error) {
},
onPageFinished: (value) {
},
navigationDelegate: (NavigationRequest request) {
print('navigationDelegate=====>>>> ${request}');
return NavigationDecision.navigate;
},
dartCallBacks: {
DartCallback(
callBack: (url) {
print('DartCallback=====>>>> $url');
},
name: 'CaptchaMessenger')
},
),

Thank you in advance.

Clarifying Security of SourceType.url.

Hi,

In this issue you say "one should NOT use it (especially with SourceType.urlBypass) for security-related stuff, such as OAuth or similar."

  1. Is this saying that we shouldn't host a page which requires normal username and password login in an Iframe?

  2. What about hosting an iframe with the normal SourceType.url, are pages that have a log in form not secure?

I am just getting confused because you said OAuth which I believe is different to a username and password login.

Great package by the way. Extremely useful and very well organised!

Thanks.

webviewx black screen in mobile view

I am trying load local asset html from assets folder in flutter and while doing so it is working fine in web, thanks webviewx team but while viewing in mobile I am seeing black spot on top and html page is not rendering at all due to import issue
`
import 'package:flutter/material.dart';
import 'package:webviewx/webviewx.dart';

class FormDetailPage extends StatefulWidget {
@OverRide
_FormDetailPageState createState() => _FormDetailPageState();
}

class _FormDetailPageState extends State {
WebViewXController webviewController;
String title = 'Form Page';

@OverRide
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: WebViewX(
javascriptMode: JavascriptMode.unrestricted,
initialContent:
'

Loading

Loading

',
initialSourceType: SourceType.HTML,
onWebViewCreated: (controller) {
webviewController = controller;
//_loadHtmlFromAssets();
},
),
);
}

}`

issue 1

can't scroll

is there an example of how to add this and allow gestures like scrolling? Setting ignoreAllGestures to false doesn't seem to do the trick. can't scroll while cursor is over the embedded content.

Html content not loading properly in mobile where as it is working fine in web?

Hi @adrianflutur , Thanks again for great package, I am facing issue with loading html content in webview, it is working fine in web but in mobile it does not work propery even with latest version.

Reproducible code:

  1. I have test,js,test.html in assets folder
    a)In test.html
<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script src='assets/test.js'></script>
</head>
<body>

<h2>Test</h2>
<div class="container">
    <div class="content">
      
        <button class="button" onclick="backClick()">Back</button>
    </div>
</div>

</body>
</html>

b) In test.js

function backClick() {
alert("js called")
}

And in flutter

_loadHtmlFromAssets() {
    webviewController.loadContent(
      AppUtils.TEST_WEB_URL,
      SourceType.HTML,
      fromAssets: true,
    );
  }

though html content is loading in mobile js function is not triggering same goes for css style as well.
Note: In web it is working fine js,html and css as well.

Intercept navigationDelegate, onLoaded...

Hi,

I would like to replace the current WebView with this to support for both mobile and web.
However, I find no way to handle the events such as navigationDelegate, onLoaded...
May I know how to do that?

Basically, I need to make sure this is work seamlessly as WebView before going further for web improvement.

Thanks!

Differences between Web and Mobile and usage limitations

Hello, here I'll try to create a helpful guide with all the differences I can find between the two and a variety of use cases when one can not bypass the intrinsic limitations of the package, since this is not really obvious from the documentation.
This will be updated from time to time. Here we go:

The Web version (iframe):

  • Can accept 3 types of content:

  1. HTML (static or from assets)
  2. normal URLs which support iframe embedding (such as https://flutter.dev/, it allows you to embed it into your iframe)
  3. normal URLs which do not support iframe embedding (such as https://google.com, you need to use SourceType.urlBypass for this). The package treats theese pages as follows:
    • It fetches the page's HTML source using a HTTP call to a Proxy server, which will remove the headers which block iframe embedding
    • It will set the HTML source to the iframe, just like we're doing at point 1) with HTML content
  • At the moment, it behaves much better than the initial version but still, one should NOT use it (especially with SourceType.urlBypass) for security-related stuff, such as OAuth or similar. This is because:

  1. As far as I know now, cookies won't work since that sourceType uses a proxy server and
  2. The default proxy servers are PUBLIC. Please at least provide your own private proxy if you want to try experimenting with this.
  • 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.

  • You can supply your own list of BypassProxy objects in the WebViewX's webSpecificParams param.

  • The headers only have effect when using SourceType.urlBypass, because that's when the package fetches the page source.

  • '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)

  • In the EmbeddedJsContent param on the WebViewX, if you define JS functions as ES6 variable functions:

    const fn = () => ..., then you also need to add them to the global scope to work: window.fn = fn .

  • URLs which redirect to another URL won't work by using SourceType.urlBypass. Redirects may work only if that website allows iframe embeddings, in which case you should use SourceType.url.

Cannot read property 'Symbol(dartx.location)' of null

hi
when i do js shell like this

document.getElementsByClassName("ad top")[0].style.display="none";

error

TypeError: Cannot read property 'style' of undefined
TypeError: Cannot read property 'Symbol(dartx.location)' of null
    at web$0._WebViewXWidgetState.new.[_updateSource] (http://localhost:54079/packages/webviewx/src/view/web.dart.lib.js:715:42)
    at http://localhost:54079/packages/webviewx/src/view/web.dart.lib.js:539:28
    at http://localhost:54079/dart_sdk.js:33443:33
    at internalCallback (http://localhost:54079/dart_sdk.js:25104:11)

but do on chrom it's ok.
my code

class WebPageState extends State<WebPage> {

  String js = '''
  document.getElementsByClassName("ad top")[0].style.display="none";
  ''';

  WebViewXController webviewController;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: WebViewX(
        initialContent: 'https://online-video-cutter.com/cn/',
        onWebViewCreated: (controller) {
          webviewController = controller;
          _evalRawJsInGlobalContext();
        },
        // jsContent: {
        //   EmbeddedJsContent(
        //     js: js
        //   )
        // },
      ),
    );
  }


  void _evalRawJsInGlobalContext() async {
    try {
      var result = await webviewController.evalRawJavascript(
        js,
        inGlobalContext: true,
      );
      print(result);
    } catch (e) {
      print(e);
    }
  }

}

How to set initial zoom level?

Hi I am kinda new to flutter and this package does help me to achieve my goal but i need to tweak it, so how can I set the zoom level for the widget when runnning on the Web? thanks

Embedded js content not working with URL source type

It seems that embedded js content only works with HTML source type, but not URL source type (at least on web platform, didn't test on Android/iOS).

Test with HTML source type (on web platform):

WebViewX(
        width: 500,
        height: 500,
        initialContent: "<h1>Hello</h1>",
        initialSourceType: SourceType.html,
        jsContent: {EmbeddedJsContent(js: "console.log('hello');")},
)

=> I can see the "hello" message in the browser's console.

Test with URL source type (on web platform):

WebViewX(
        width: 500,
        height: 500,
        initialContent: "https://httpbin.org/get",
        initialSourceType: SourceType.url,
        jsContent: {EmbeddedJsContent(js: "console.log('hello');")},
)

=> no message in console

Tracking support status for desktop

This is a thread for tracking implementation status for this package on Windows, Linux and MacOS.
It will also be used to point to news, commits, branches or merges related to this.
Feel free to comment if you have any news or thoughts.

Current status:

Windows - not implemented

See flutter/flutter#31713
There are two projects that claim to be doing this, but they are not using platform-views.

  1. https://github.com/jnschulze/flutter-webview-windows (most likely to be adopted if platform-views take too long)
    (there was this PR (#17) but the package must be abstracted first + other reasons which are in the PR)
  2. https://github.com/clarkezone/flutter_win_webview
Linux - not implemented

See flutter/flutter#41724.

There seems to be a PR for this here flutter/engine#26288

MacOS - not implemented

See flutter/flutter#41722

[Web] Loading local Angular app with urlBypass through local proxy

Hi,

I'm trying to load an Angular app on the web using urlBypass. I set my own local proxy with nginx.
I'm using urlBypass cause I need to inject JS into my Angular app.
When I access to my app through my proxy using a browser, my app is loaded fully.
When I access to my app through the webview, I only get the raw HTML.

I read through #27 and I saw:

normal URLs which do not support iframe embedding (such as https://google.com, you need to use SourceType.urlBypass for this). The package treats theese pages as follows:
It fetches the page's HTML source using a HTTP call to a Proxy server, which will remove the headers which block iframe embedding
It will set the HTML source to the iframe, just like we're doing at point 1) with HTML content

So my understanding is that when using urlBypass we can only get plain HTML ? Or should I change my proxy so it returns more than the HTML ?

get URL of a new page

Hi,

How can I get informed about the new URL (full path) when I'm inside a WebView (Flutter Web project) and I clicked on a button (on the webpage) and got transmitted to a new webpage?

Thanks,
Lior

Cookies are not enabled

Hi,

Im prevented to load some pages because it says cookies aren't enabled and I haven't find a way to enable them.

Null safe

Are you considering adding in null safe? Or is there a way to run this in a project using Null safety?

Javascript Bridge Problems on the Web

// in dart

dartCallBacks: Set.from([
DartCallback(
name: "test",
callBack: (message) {
showSnackBar('${message.toString()}', context);
},
),
]),

void _evalRawJsInGlobalContext() async {
try {
var result = await _wc.evalRawJavascript(
"jstestFunction('test')",
);
} catch (e) {
showAlertDialog("${e}", context);
}
}

// in html

<script> function jstestFunction(text){ console.log(text); if (window.test && window.test.postMessage) { console.log(document.documentElement.innerHTML); test.postMessage(text); test(text); } } </script>

// output
mobile : 'test'
web : nosuchmethod: invalid member on null:'callmethod'

I want to show the values ​​returned by the web using dartcallback and evalRawJavascript. Obviously, it works well on mobile, but when I run it on the web, I get an error message saying nosuchmethod: invalid member on null:'callmethod'. If I misunderstand and use it strangely, please explain how to fix it. Or, if you can't support it on the web, please tell us.

This article uses Google Translator.
thank you.

Microphone and camera doesn't work

Hey!
I'm trying to open a jitsi meeting inside an WebViewX:

WebViewX(
    webSpecificParams: WebSpecificParams(additionalAllowOptions: [
        'camera',
        'microphone',
    ]),
    initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.alwaysAllow,
    initialContent: this.widget.url, // https://meet.jit.si/somerandomtext
    initialSourceType: SourceType.url,
    onWebViewCreated: (controller) => webviewController = controller,
    height: maxHeight,
    width: maxWidth,
);

This is what I get on web:
Screenshot from 2021-12-30 13-59-55
And this is what I get on mobile (android):
Screenshot from 2021-12-30 14-01-07
None of that want to work.

I have added the following to AndroidManifest.xml

    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
    <uses-permission android:name="android.permission.CAMERA"/>

Larger HTML files cause exception

I was able to load small HTML markup from string, but failed with a 20kb file. This error is thrown about half way through the file, after page started loading. (The same file works fine in webview plugin)

Code loading the file:

Future<void> getHtml() async {
    String c = await rootBundle.loadString('assets/index.html');
    webviewController.loadContent(c, SourceType.html);
  }

Exception thrown:

════════ Exception caught by foundation library ════════════════════════════════
The following RangeError was thrown while dispatching notifications for WebViewXController:
RangeError (end): Invalid value: Not in inclusive range 0..19853: -1

When the exception was thrown, this was the stack
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 251:49  throw_
dart-sdk/lib/core/errors.dart 338:9                                           checkValidRange
dart-sdk/lib/_internal/js_dev_runtime/private/js_string.dart 168:22           substring]
packages/webviewx/src/utils/html_utils.dart 208:36                            embedInHtmlSource
packages/webviewx/src/utils/html_utils.dart 173:12                            embedJsInHtmlSource
packages/webviewx/src/utils/html_utils.dart 235:12                            embedWebIframeJsConnector
packages/webviewx/src/utils/html_utils.dart 64:14                             preprocessSource
packages/webviewx/src/view/impl/web.dart 420:35                               [_updateSource]
packages/webviewx/src/view/impl/web.dart 381:5                                [_handleChange]
packages/flutter/src/foundation/change_notifier.dart 308:24                   notifyListeners
packages/webviewx/src/controller/impl/web.dart 265:5                          [_notifyWidget]
packages/webviewx/src/controller/impl/web.dart 110:5                          loadContent
dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 84:54            runBody
dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 123:5            _async
packages/webviewx/src/controller/impl/web.dart 82:27                          loadContent
packages/wsm2/src/map/map_view.dart 108:23                                    getHtml
dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 45:50            <fn>
dart-sdk/lib/async/zone.dart 1687:54                                          runUnary
dart-sdk/lib/async/future_impl.dart 160:18                                    handleValue
dart-sdk/lib/async/future_impl.dart 767:44                                    handleValueCallback
dart-sdk/lib/async/future_impl.dart 796:13                                    _propagateToListeners
dart-sdk/lib/async/future_impl.dart 602:5                                     [_completeWithValue]
dart-sdk/lib/async/future_impl.dart 640:7                                     callback
dart-sdk/lib/async/schedule_microtask.dart 40:11                              _microtaskLoop
dart-sdk/lib/async/schedule_microtask.dart 49:5                               _startMicrotaskLoop
dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 166:15           <fn>
The WebViewXController sending notification was: Instance of 'WebViewXController'
════════════════════════════════════════════════════════════════════════════════

Оn the Stack Widget I can't use buttons on Tap Event

`

@OverRide
Widget build(BuildContext context) {
// if (isImageloaded == false) {
// return LoadingWidget();
// }
return Scaffold(
endDrawer: const NavigationPage(),
body: LayoutBuilder(builder: (context, constraints) {
return Stack(
children: [
_backgroundMapField(constraints),
SoundAndMenuWidget(
icons: isSoundOn ? Icons.volume_up : Icons.volume_mute,
onTapVolume: isSoundOn
? () {
setState(() {
isSoundOn = !isSoundOn;
backgroundplayer.pause();
});
}
: () {
setState(() {
isSoundOn = !isSoundOn;
backgroundplayer.play();
});
},
onTapMenu: () {
Scaffold.of(context).openEndDrawer();
},
),
],
);
}),
);
}

Widget _backgroundMapField(BoxConstraints constraints) {
return Container(
color: Colors.transparent,
child: WebViewX(
width: constraints.maxWidth,
height: 0.863 * constraints.maxHeight,
initialContent: '

Hello, world!

',
initialSourceType: SourceType.html,
onWebViewCreated: (controller) {
webviewController = controller;
webviewController.loadContent(
"""




<title>Map</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bodymovin/5.7.4/lottie.min.js"></script>
<style>
html, body {
margin: 0;
padding: 0;
}
img {
display: block;
width: 100%;
}
#lottie {
position: absolute;
left: 0;
top: 0;
z-index: 2;
}
</style>
<style type="text/css"> flt-scene-host { pointer-events: auto !important; } </style>




<script>
var animation = bodymovin.loadAnimation({
container: document.getElementById('lottie'),
path: 'assets/map_lottie/map_01/data.json',
renderer: 'svg',
loop: false,
autoplay: true
});
</script>


""",
SourceType.html,
);
},
),
);
}

import 'package:flutter/material.dart';

class SoundAndMenuWidget extends StatelessWidget {
final Function() onTapVolume;
final Function() onTapMenu;
final IconData icons;
final Color? color;
final Widget? widget;
const SoundAndMenuWidget(
{Key? key,
required this.onTapVolume,
required this.icons,
this.color,
this.widget,
required this.onTapMenu})
: super(key: key);

@OverRide
Widget build(BuildContext context) {
return Align(
alignment: Alignment.topCenter,
child: Container(
padding: const EdgeInsets.fromLTRB(24, 24, 24, 0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
onPressed: onTapVolume,
icon: Icon(icons),
color: color ?? Colors.black,
),
widget ?? const SizedBox(),
IconButton(
onPressed: onTapMenu,
icon: Icon(
Icons.menu,
color: color ?? Colors.black,
))
],
),
),
);
}
}

`

Problem with Drawer

Drawer not close on click over webview.

setIgnoreAllGestures reload the page

Page height issue, not showing all the url page.

I am using the navigationDelegate to show the link in a new scaffold using showCupertinoModalBottomSheet(like Instagram), but I am not able to scroll to the bottom of the page. I am able to scroll only to the height of the MediaQuery.of(context).size.height

Result:

Registrazione.schermo.2021-10-30.alle.09.38.33.mov

Code to launch the new page for the link

  showCupertinoModalBottomSheet(
                          context: context,
                          builder: (context) => Container(
                            height: MediaQuery.of(context).size.height / 1.5,
                            child: NewsItemDisplayLink(
                              sourceType: navigation.content.sourceType,
                              source: navigation.content.source,
                            ),
                          ),
                        );

Widget Page:

CupertinoPageScaffold(
      backgroundColor: Style.backgroundColor(context),
      navigationBar: CupertinoNavigationBar(
        backgroundColor: Style.menuColor(context),
        padding: EdgeInsetsDirectional.only(bottom: 1),
        leading: IconButton(
          icon: Icon(
            CupertinoIcons.clear,
            color: Style.primaryColor,
            size: 30,
          ),
          onPressed: () => Navigator.of(context).pop(),
        ),
        trailing: StatefulBuilder(
          builder: (BuildContext context, StateSetter settingState) => Row(
            mainAxisSize: MainAxisSize.min,
            children: [
              IconButton(
                icon: Icon(
                  CupertinoIcons.back,
                  color: Style.primaryColor,
                  size: 30,
                ),
                onPressed: () async {
                  if (await webviewLinkController.canGoBack()) {
                    webviewLinkController.goBack();
                  }
                },
              ),
              IconButton(
                icon: Icon(
                  CupertinoIcons.forward,
                  color: Style.primaryColor,
                  size: 30,
                ),
                onPressed: () async {
                  if (await webviewLinkController.canGoForward()) {
                    webviewLinkController.goForward();
                  }
                },
              ),
              IconButton(
                icon: Icon(
                  linkSaved
                      ? CupertinoIcons.bookmark_fill
                      : CupertinoIcons.bookmark,
                  color: Style.primaryColor,
                  size: 30,
                ),
                onPressed: () async {
                  //TODO implementare salva link
                  setState(() => linkSaved = !linkSaved);
                  EasyLoading.showToast(
                      linkSaved
                          ? 'Link aggiunto ai salvati'
                          : 'Link rimosso dai salvati',
                      duration: Duration(seconds: 2),
                      dismissOnTap: true,
                      toastPosition: EasyLoadingToastPosition.bottom);
                },
              ),
            ],
          ),
        ),
      ),
      child: SafeArea(
        child: SingleChildScrollView(
          child: Container(
            child: WebViewX(
              initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.alwaysAllow,
              initialContent: widget.source,
              initialSourceType: SourceType.urlBypass,
              onWebViewCreated: (controller) {
                webviewLinkController = controller;
                setState(() => loading = false);
              },
              height:
                  screenSize.height, //HERE is the problem
              width: screenSize.width,
            ),
          ),
        ),
      ),
    )

Session data not preserved in web

I prserved a data in the session variable,
$_SESSION['final_amount'] = some_value
but when it is accessed in another page in the same webview says undefined final_amount

Issue in opening google.com while using URLBypass in flutter webviewx on flutter web

Hey I am using flutter web 2.0.4 stable

image

Getting this error while accessing google.com or any other web site using URLBypass

image
here is my code

My flutter doctor:
[✓] Flutter (Channel stable, 2.0.4, on Linux, locale
en_GB.UTF-8)
[✓] Android toolchain - develop for Android devices (Android SDK version
30.0.0-rc1)
[✓] Chrome - develop for the web
[!] Android Studio (not installed)
[✓] Connected device (1 available)

Javascript channel communication

Hello,

thank you for developing this package. It has really large set of functionalities and it's well documented.

But I miss one thing: how can I bilateral communication using Javascript channels? There is no property for that in WebViewX widget. My use case is: on website there is postMessage call, which I have to capture. How can I achieve it with this package?

Of course I found TestDartCallback in example, but in my case I won't have option to edit website code to add these callbacks.

Cant Run or Build Project After Flutter 2.2 Upgrade

C:\Flutter.pub-cache\hosted\pub.dartlang.org\webview_flutter-2.0.6\android\src\main\java\io\flutter\plugins\webviewflutter\InputAwareWebView.java:31:
error: cannot find symbol
private ThreadedInputConnectionProxyAdapterView proxyAdapterView;
^
symbol: class ThreadedInputConnectionProxyAdapterView
location: class InputAwareWebView
C:\Flutter.pub-cache\hosted\pub.dartlang.org\webview_flutter-2.0.6\android\src\main\java\io\flutter\plugins\webviewflutter\InputAwareWebView.java:112: error: cannot find symbol
new ThreadedInputConnectionProxyAdapterView(
^
symbol: class ThreadedInputConnectionProxyAdapterView
location: class InputAwareWebView
Note: Some input files use or override a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
2 errors

FAILURE: Build failed with an exception.

  • What went wrong:
    Execution failed for task ':webview_flutter:compileReleaseJavaWithJavac'.

Compilation failed; see the compiler error output for details.

  • Try:
    Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

  • Get more help at https://help.gradle.org

BUILD FAILED in 53s

on web google.com or any other URL not launching properly it says "url name " refused to connect and screen fluctuates

child: webx.WebViewX(
key: ValueKey('webx'),
onWebResourceError: (error) {
print('error yha h $error');
},
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
// initialContent: 'cuddi',
initialSourceType: webx.SourceType.urlBypass,
javascriptMode: webx.JavascriptMode.unrestricted,
onWebViewCreated: (controller) {
webviewController = controller;
webviewController.loadContent(
"https://google.co.in", webx.SourceType.urlBypass,
// headers: {'token': state.paymentToken}, body: context
);
},
)

also I. added a / after link but nothing changed

showDialog() while a WebViewX is on screen disables alert buttons

I have an app that loads some HTML from assets on first launch as the body of a Scaffold, and while this HTML is being displayed, I found that when I call showDialog(), all of the buttons in the alert are not clickable, and I am still somehow able to interact with the HTML behind it.

Rough example:

Scaffold(
    body: WebViewX(
        onWebViewCreated: (controller) {
        webviewController = controller;
        webviewController.loadContent(
           'assets/welcome.html', SourceType.HTML,
            fromAssets: true);
           },
       )
)

The alert is standard

showDialog(
          context: context,
          builder: (context) {
            return WillPopScope(
              onWillPop: () async => barrierDismissable,
              child: AlertDialog(
                title: Text(title),
                content: content,
                actions: actions,
              ),
            );
          })```

Could this be my implementation or has someone seen something similar?

error: LateInitializationError: Field 'connector' has not been initialized.

Hello,

I followed your example for running JS code from dart:
I've added this code:

 return WebViewX(
      width: 320,
      height: 500,
      initialContent:
          'someUrl',
      onWebViewCreated: (controller) => webviewController = controller,
      onPageFinished: (_) {
        webviewController
            .callJsMethod('testPlatformSpecificMethod', <dynamic>['Hi']);
      },
      jsContent: const {
        EmbeddedJsContent(
          webJs:
              "function testPlatformSpecificMethod(msg) { CaptchaMessenger('Web callback says: ' + msg) }",
          mobileJs:
              "function testPlatformSpecificMethod(msg) { CaptchaMessenger.postMessage('Mobile callback says: ' + msg) }",
        ),
      },
      dartCallBacks: {
        DartCallback(callBack: recaptchaCallback, name: 'CaptchaMessenger')
      },
    );

But when I run this code I always get this error:
error: LateInitializationError: Field 'connector' has not been initialized..

Additional info:

  • I'm running the app on Web

after use main branch code,run js on chrom still have a minor problem.

code

class WebPageState extends State<WebPage> {

//   String js = '''
//   var remove=n=>{n.split(",").forEach(v=>{if(v.indexOf("@ID(")==0){document.getElementById(v.substring(4,v.length-1)).style.display="none"}else{for(let e of document.getElementsByClassName(v))e.style.display="none"}})}
// remove("ad top")
//   ''';

  String js = '''
  document.getElementsByClassName("ad top")
  ''';

  WebViewXController webviewController;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: WebViewX(
        initialContent: 'https://online-video-cutter.com/cn/',
        initialSourceType: SourceType.URL_BYPASS,
        onWebViewCreated: (controller) {
          webviewController = controller;
          _evalRawJsInGlobalContext();
        },
        // jsContent: {
        //   EmbeddedJsContent(
        //     js: js,
        //   ),
        //   // EmbeddedJsContent(
        //   //   webJs:
        //   //   "function testPlatformSpecificMethod(msg) { TestDartCallback('Web callback says: ' + msg) }",
        //   //   mobileJs:
        //   //   "function testPlatformSpecificMethod(msg) { TestDartCallback.postMessage('Mobile callback says: ' + msg) }",
        //   // ),
        // },
      ),
    );
  }


  void _evalRawJsInGlobalContext() async {
    try {
      // webviewController.loadContent(
      //   'https://online-video-cutter.com/cn/',
      //   SourceType.URL,
      // );
      Future.delayed(Duration(seconds: 4),() async {
        var result = await webviewController.evalRawJavascript(js);
        print(result);
      });
    } catch (e) {
      print(e);
    }
  }

}

even if don't do any js shell,the console print

DOMException: Failed to set the 'href' property on 'Location': The current window does not have permission to navigate the target frame to 'about:srcdoc'.
    at HTMLDocument.<anonymous> (https://static.123apps.com/js/shared-v3.js?6eeaf:1:264239)
[object HTMLCollection]

and the web page is't show normal. such as this link https://online-video-cutter.com/cn.

Can't play video in youtube through webviewx

When I try I receive this:

Error: URI is missing protocol: about:srcdoc
    at wh (https://www.youtube.com/s/player/2fa3f946/player_ias.vflset/en_US/base.js:422:311)
    at Eh (https://www.youtube.com/s/player/2fa3f946/player_ias.vflset/en_US/base.js:441:42)
    at VK.vv.ww (https://www.youtube.com/s/player/2fa3f946/player_ias.vflset/en_US/base.js:6520:174)
    at VK.ww (https://www.youtube.com/s/player/2fa3f946/player_ias.vflset/en_US/base.js:7204:390)
    at baa.l (https://www.youtube.com/s/player/2fa3f946/player_ias.vflset/en_US/base.js:3025:281)
    at Aa (https://www.youtube.com/s/player/2fa3f946/player_ias.vflset/en_US/base.js:55:40)
    at daa.next (https://www.youtube.com/s/player/2fa3f946/player_ias.vflset/en_US/base.js:56:92)

Difficulty in using webview

Hey! The example app looks kinda complex for my use case and I can't seem to get the webviewx plugin working. I want to embed a webpage in a container in Flutter web, here's what my code currently looks like.

Container(
    height: 520,
    width: 780,
    decoration: BoxDecoration(
      borderRadius: BorderRadius.circular(25),
),  child: WebViewX(
    initialContent: '<h2> Hello, world! </h2>',
    initialSourceType: SourceType.html,
    onWebViewCreated: (controller) => webviewController = controller,
),  

I want the initial content to be the webpage I plan to embed itself, the idea is that a user will click an icon and it will bring up a webpage. I tried changing the initialContent parameter to a link and the initialSourceType parameter to SourceType.url but that gives me an unexpected null value error when running build.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.