Flutter WebView 和JavaScript 交互

4,272 阅读1分钟

flutter官方并没有提供WebView组件,不过官方的plugin库给我们提供了webview_flutter插件,集成到我们的项目中就可以使用了

一、集成

在项目的pubspec.yaml文件中添加一下代码
webview_flutter: ^0.3.19+6

二、简单使用

var webUrl ="http://192.168.10.120:8081/test.html?time=${DateTime.now()};
return Scaffold(
    appBar: AppBar(title: Text('WebView')),
    body: Builder(builder: (BuildContext context) {
      return WebView(
        initialUrl: webUrl,
        onWebViewCreated: (WebViewController controller) {
          _controller = controller;
        },
        onPageStarted: (String url) {},
        onPageFinished: (String url) {},
      );
    }));
    }

三、Js 调用Flutter(两种方法)

方法一、使用javascriptChannels发送消息

  • Flutter代码

  • 定义javascriptChannels JavascriptChannel方法中的两个参数,name是和js协商的标记,onMessageReceived当js收到Flutter发送的消息时的回调

      JavascriptChannel _alertJavascriptChannel(BuildContext context) {
          return JavascriptChannel(
                  name: 'MyApp',
                  onMessageReceived: (JavascriptMessage message) {
                  //js 收到了Flutter发送的消息
                  showToast(message.message);
                });
          }
          
      WebView(
      javascriptChannels: <JavascriptChannel>[
          _alertJavascriptChannel(context),
      ].toSet(),
    
  • js代码

      <button onclick="callFlutter()">callFlutter</button>
          function callFlutter(){
          // MyApp为Flutter定义的name
          MyApp.postMessage("JS调用了Flutter");
      }
    

方法二、使用路由委托navigationDelegate拦截url

  • Flutter代码 在Flutter端,我们就可以在navigationDelegate回调中拦截这个符合js://webviewscheme的路由地址了:

      navigationDelegate: (NavigationRequest request) {
          if (request.url.startsWith('js://html')) {
            showToast('JS调用了Flutter By navigationDelegate');
            print('blocking navigation to $request}');
            return NavigationDecision.prevent;  //阻止路由替换
          }
          print('allowing navigation to $request');
          return NavigationDecision.navigate; //允许路由替换
        },
    
  • js代码

      <button onclick="callFlutter()">callFlutter</button>
      function callFlutter(){
      var url = "js://html?params1=111&params2=222"
      //url 为两端定义的协议
      document.location = url;
      }
    

四、Flutter 调用JS

  • 在实际开发中免不了和js的数据交互,例如js需要拿到app登录的用户信息,这时候我们在Flutter端收到请求以后,可以通过拿到一个WebViewController,通过它的evaluateJavascript()方法,然后将我们的用户信息传递给js,具体代码如下:

  • Flutter 代码

      onWebViewCreated: (WebViewController webViewController) {
      _controller = webViewController;
      },
      JavascriptChannel _alertJavascriptChannel(BuildContext context) {
          return JavascriptChannel(
                  name: 'MyApp',
                  onMessageReceived: (JavascriptMessage message) {
                  showToast(message.message);
                  // js 告诉Flutter需要获取用户信息
                  if(message.messageType == "getUserInfo"){
                      //Flutter把用户信息传递到js
                      var userInfo = User("username","token");
                      String userString = json.encode(userInfo);
                      controller
                      ?.evaluateJavascript('getUserInfo($userString)')
                      ?.then((result) {
                          
                      });
                      }
                });
          }
    
  • js 代码

      <button onclick="callFlutter()">获取用户信息</button>
      <script>
          function callFlutter() {
              // 我需要用户信息
              var str = navigator.userAgent;
              if (str.includes('MyApp')) { // 此处为Flutter端JavascriptChannel定义的name
                  MyApp.postMessage(JSON.stringify({ method: "getUserInfo" }));
              } else {
                  $('body').html('<p>hello world</p>');
              }
          }
    
          function getUserInfo(message) {
              var data = JSON.parse(JSON.stringify(message)) //注意此处json的解析方式
              document.getElementById("p1").textContent = data.userUid;
              document.getElementById("p2").textContent = data.username;
              document.getElementById("p3").textContent = data.userType;
              document.getElementById("p4").textContent = data.realName;
          }
      </script>
    

五、补充

  • 在实际项目中,我们需要和js开发者定义交互的数据结构,例如:

    class JsBridgeModel {
        int type; //类型 例如 打开微信、获取用户信息等
        String message;
        var data;
    }
    
    JsBridgeModel(this.type,this.message,this.data);
    js端post发送消息的时候需要根据Flutter定义好的格式来
    XX.postMessage(JSON.stringify({ type: 1,message:"",data:{} }))
    

五、总结:

  • Flutter和js交互的方式基本就是这样,如果你还有需要补充的可以在下方留言。