Flutter 插件 webview_flutter 使用指北

11,039 阅读2分钟

现状

目前主流有两个插件,flutter_webview_pluginwebview_flutterflutter_webview_plugin 是一个第三方插件,文档会比较全,star 数也最多。但是使用过程中有两个主要问题:

  • 不能在JS中调用Flutter方法
  • 不能在H5进入某个URL之前拦截

所以本文使用官方的 webview_flutter,目前还在预览版,期待更多的功能

安装插件

pubspec.yaml

dependencies 增加 webview_flutter: ^0.3.13,有最新的使用最新版本,

dependencies:
  webview_flutter: ^0.3.13

info.list

路径:ios/Runner/info.list,添加配置:

<key>io.flutter.embedded_views_preview</key>
<string>YES</string>

<key>NSAppTransportSecurity</key>
<dict>
	<key>NSAllowsArbitraryLoads</key>
	<true/>
	<key>NSAllowsArbitraryLoadsInWebContent</key>
	<true/>
</dict>

使用

参数一览

参数名 类型 默认值 说明
initialUrl String ''
onWebViewCreated Function WebView创建完成时调用
javascriptMode JavascriptMode JavascriptMode.disabled JS执行模式 默认是不调用
javascriptChannels Set 使用javascriptChannel JS可以调用Flutter
navigationDelegate Function 拦截请求
onPageFinished Function 页面加载完成调用
gestureRecognizers Set 手势

场景

最简单的地加载一条url

WebView(
  initialUrl: "https://flutterchina.club/",
  //JS执行模式 是否允许JS执行
  javascriptMode: JavascriptMode.unrestricted
)

h5 调用 flutter -- JavascriptChannel

// 往h5 window 里面插入全局方法 Toaster
JavascriptChannel _toastJavascriptChannel(BuildContext context) {
  return JavascriptChannel(
    name: 'Toaster',
    onMessageReceived: (JavascriptMessage message) {
      print(message);
      Fluttertoast.showToast(
        msg: message.message
      );
    });
}

// 往 Webview 组件注册 javascriptChannels
new WebView(
  ...
  javascriptChannels: <JavascriptChannel>[ //javascriptChannels这个是api提供的互调的方法,
    _toastJavascriptChannel(context),
  ].toSet()
)

// js 调用
Toaster.postMessage('js call flutter success!!')

h5 调用 flutter -- navigationDelegate

new WebView(
  ...
  navigationDelegate: (NavigationRequest request) {
  // print('navigationDelegate: ${request.url}');
  if(request.url.indexOf('m=webview') > -1) {
    String _url = helper.addUrlParam(request.url.replaceAll('&m=webview', ''));
    Navigator.of(context).push(new MaterialPageRoute(builder: (_) {
      return Browser(
        title: ' ',
        url: _url
      );
    }));
    return NavigationDecision.prevent;
  }
)

flutter 调用 h5

// 在页面加载完毕,修改 Webview 的标题
new WebView(
  ...
  onPageFinished: (url) {
    // 设置标题
    _controller.evaluateJavascript("document.title").then((result){
      print(result);
      setState(() {
        widget.title = result;
      });
    });
  },
)

完整组件

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:fluttertoast/fluttertoast.dart';

import '../helper/index.dart' as helper;

class Browser extends StatefulWidget {
  String url;
  String title;

  Browser({this.url, this.title});

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

class _BrowserState extends State<Browser> {
  dynamic _controller;

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

  @override
  void dispose() {

    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    JavascriptChannel _toastJavascriptChannel(BuildContext context) {
      return JavascriptChannel(
        name: 'Toaster',
        onMessageReceived: (JavascriptMessage message) {
          print(message);
          Fluttertoast.showToast(
            msg: message.message
          );
        });
    }

    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Stack(
        children: <Widget>[
          new WebView(
            initialUrl: widget.url,
            javascriptMode: JavascriptMode.unrestricted,
            onWebViewCreated: (controller) {
              _controller = controller;
            },
            /**
             * 当 url 带有 'm=webview',则打开新的webview
             */
            navigationDelegate: (NavigationRequest request) {
              // print('navigationDelegate: ${request.url}');
              if(request.url.indexOf('m=webview') > -1) {
                String _url = helper.addUrlParam(request.url.replaceAll('&m=webview', ''));
                Navigator.of(context).push(new MaterialPageRoute(builder: (_) {
                  return Browser(
                    title: ' ',
                    url: _url
                  );
                }));
                return NavigationDecision.prevent;
              }

              return NavigationDecision.navigate;
            },
            onPageFinished: (url) {
              // 设置标题
              _controller.evaluateJavascript("document.title").then((result){
                print(result);
                setState(() {
                  widget.title = result;
                });
              });

              // var a = '123';
              // print(a+1);

              // 测试flutter 调用我页面的方法
              // _controller.evaluateJavascript("callJSFunc();").then((result){
              //   // print('callJSFunc has called: $result');
              // });
            },
            javascriptChannels: <JavascriptChannel>[ //javascriptChannels这个是api提供的互调的方法,
              _toastJavascriptChannel(context),
            ].toSet()
          )
        ],
      ),
    );
  }
}

todo

  • 接入cordova
  • 获取设备的网络状态

参考文章