前言

flutter开发过程中难免会用到 WebView,且有时分还会或许需求与 js 交互,而体系给咱们供给一个一个WebView库房,现在运用杰出(前面版本的许多问题根本都现已处理,能够定心食用)

官方库房地址

但仅仅是官方的事例,很多地方读者也不明白或许打不通,本篇文章便是打通后的成果,且代码库房有两个端的代码,能够测验,因此能够定心食用,且里边还加了进展条,作用杠杠的

事例地址(包含react测试用例和flutter事例)

WebView参数简介

  • onWebViewCreated:在 WebView 创立完结后调用,只会被调用一次;
  • initialUrl:url 不多说;
  • initialCookies:设置 cookie,web端比较常用,一般用于带着用户令牌
  • javascriptMode:JS 履行形式(是否允许 JS 履行),默认为JavascriptMode.disable只能履行静态页面,设置为JavascriptMode.unrestricted即可处理;
  • gestureNavigationEnabled:侧边手势是否敞开,敞开后从左往右划动能够退出当时页面
  • onPageStarted:页面开端加载
  • onPageFinished:WebView 加载完毕时的回调
  • onProgress:进展条百分比回调(回来 int 类型,0~100),当然仅仅表示静态页的加载进展,里边 js 履行成果不知道
  • navigationDelegate:路由托付(导航署理),能够经过阻拦跳转url来完成,能够跳转flutter或许交互,也能够传递参数;
  • javascriptChannels:JS 和 Flutter 通信的 Channel set;

WebView事例详解

话不多说,直接上代码,下面有各个参数的阐明,一同也会具体介绍几个参数

WebView(
  //url
  initialUrl: widget.url,
  //允许js履行,默认不允许,显现静态页面
  javascriptMode: JavascriptMode.unrestricted,
  //侧边手势是否敞开,敞开后从左往右划动能够退出当时页面
  gestureNavigationEnabled: true,
  //webview创立完结后的回调,只会回调一次
  onWebViewCreated: (WebViewController webViewController) {
    //webView创立完结,保存以便于后续时分
    _controller = webViewController;
  },
  //页面开端加载
  onPageStarted: (String url) {
    print('Page started loading: $url');
  },
  //页面加载完毕
  onPageFinished: (String url) async {
    print('Page finished loading: $url');
    //顺表加载一个导航
    final title = await _controller?.getTitle();
    if (title != null) {
      setState(() {
        webTitle = title;
      });
    }
  },
  //进展条刚好再开端和结束之间
  onProgress: (int progress) {
    print('WebView is loading (progress : $progress%)');
    setState(() {
      progessValue = progress / 100.0;
    });
  },
  //路由托付(导航署理),能够经过阻拦跳转url来完成,能够跳转flutter或许交互,也能够传递参数
  navigationDelegate: (NavigationRequest request) {
    print(request.url);
    if (request.url.startsWith('https:///post/7155821382742310920')) {
      print("进入了我的侧边栏文章,暂时不作处理,就符号一下");
    }
    // else if (request.url.startsWith('https:///post/')) {
    //   //阻止进入其他文章,就只能进入我上面一篇文章
    //   print('blocking navigation to $request}');
    //   return NavigationDecision.prevent;
    // }
    //其他恳求正常跳转
    return NavigationDecision.navigate;
  },
  //JavascriptChannel来进行交互
  javascriptChannels: <JavascriptChannel>{
    //参数为Set,能够传入多个JavascriptChannel,依据name作为哈希值
    JavascriptChannel(
      name: 'flutterMessage',
      onMessageReceived: (JavascriptMessage message) {
        print("flutter接纳到了web端发送过来的flutterMessage音讯${message.message}"); //js发送过来的信息
      },
    ),
    JavascriptChannel(
      name: 'flutterOrder',
      onMessageReceived: (JavascriptMessage message) {
        //js发送过来的信息,咱们能够进行处理或许跳转等
        print("flutter接纳到了web端发送过来的flutterOrder音讯${message.message}"); //js发送过来的信息
        _controller?.loadUrl("https:///ios");
      },
    ),
  },
),

onProgress

进展回调,里边回来的是 0~100的数字,一般作为百分比运用,当咱们进展条的时分,需求合理操控其值

因为下面的进展条值为 0~1之间,因此体系的回调的值需求除以100方可运用

//滚动条,用来显现加载进展,下面是线性的,圆形的是CircularProgressIndicator
//能够运用完毁掉(本事例便是)
LinearProgressIndicator(
  color: Colors.greenAccent,
  backgroundColor: Colors.transparent,
  value: progessValue,
),
//进展条回调
onProgress: (int progress) {
  print('WebView is loading (progress : $progress%)');
  setState(() {
    progessValue = progress / 100.0;
  });
},

initialCookies

设置 cookie web开发中一般都会用到 cookie 信息,需求登录方可获取,手机端现已登录的状况,假如访问web的一些子页面webView,那就需求带着cookie信息了,因此需求咱们手动传递cookie 信息然后加载 webView

另外,手机一般只要一个用户登录,必要的话,能够将 webView 直接跟自己的用户信息 衔接封装到一同,这样就不用每次翻开 webView,都要重新填写 cookie 信息啦

initialCookies: const <WebViewCookie>[
    //一般一个网页用同一组cookie,支持多组cookie
    WebViewCookie(
      name: "token", //cooke键值
      value: "1293172938712931798", //cookie值
      domain: ".", //域名
      //path: "/",//一般页面都是运用一个cookie,一般'/'运用到整个运用
    ),
    WebViewCookie(
      name: "username", //cooke键值
      value: "www.baidu.com", //cooke值
      domain: ".", //域名
      //path: "/",//一般页面都是运用一个cookie,一般'/'运用到整个运用
    ),
],

WebViewController

WebViewControllerwebView 在运用的过程中非常常用的一个目标,里边有很多的参数,例如:例如滚动到某个位置、判断网页是否能回来、查找页面标题、跳转、设置 cookie 等

这里边咱们只介绍是否能回来和找出标题

onWebViewCreated

其为创立 webview 后的回到,咱们能够保存 webViewController

//webview创立完结后的回调,只会回调一次
onWebViewCreated: (WebViewController webViewController) {
    //webView创立完结
    _controller = webViewController;
},

onPageFinished

页面加载完毕后的回调,咱们能够在加载完结之后,获取标题,显现到咱们的导航上面

//页面加载完毕,会发现咱们将办法声明成了异步,这样方便取出title,当然也能够用 then 回调获取
onPageFinished: (String url) async {
  print('Page finished loading: $url');
  //顺表获取导航标题,回来的是 Future
  final title = await _controller?.getTitle();
  if (title != null) {
    setState(() {
      webTitle = title;
    });
  }
},

goBack回来上一页

当咱们web只要一个页面的时分,咱们直接回来退出 web 页面就好了,假如 web 中也有路由和子页面,咱们点击回来的时分,直接退出了整个 web 页面,那么体会就不是很好了,因此体系给咱们供给了 canGoBack办法,咱们能够经过该办法直接获取是否能回来上一页,到 web 跟路由时 就不能回来,走咱们的导航便是了

leading: Builder(
  builder: (context) {
    return IconButton(
      onPressed: () {
        if (_controller != null) {
          _controller?.canGoBack().then((value) {
            if (value) {
              _controller?.goBack();
            }else {
              Navigator.of(context).pop();
            }
          }).catchError((err) {
            Navigator.of(context).pop();
          });
        }else {
          Navigator.of(context).pop();
        }
      },
      icon: const Icon(
        Icons.arrow_back_ios_new,
        color: Colors.white,
      ),
    );
  },
),

你或许觉得上面的判断有点多,没办法便是要谨慎,下面优化下 onPress 逻辑,下面是不是明晰一些了

//运用闭包或许写外面都行
Future<void> canGoBack() async {
  if (_controller != null && await _controller!.canGoBack()) return;
  throw Error();
}
canGoBack().then((value) {
  _controller?.goBack();
}).catchError((error) {
  Navigator.of(context).pop();
});

loadUrl(加载新页面)

假如想无缝跳转到其他 web 页面,能够采用 loadUrl 办法,这样就能够无缝衔接咱们的多个 web 运用了

_controller?.loadUrl("https:///ios");

navigationDelegate路由托付

经过路由托付,咱们能够直接操控页面是否进行跳转,一同也能够接纳 web 页面传递过来的信息,如下所示

//路由托付(导航署理),能够经过阻拦跳转url来完成,能够跳转flutter或许交互,也能够传递参数
navigationDelegate: (NavigationRequest request) {
  print(request.url);
  if (request.url.startsWith('https:///post/7155821382742310920')) {
    print("进入了我的侧边栏文章,暂时不作处理,就符号一下");
  }else if (request.url.startsWith('https:///post/')) {
    //阻止进入其他文章,就只能进入我上面一篇文章
    print('blocking navigation to $request}');
    return NavigationDecision.prevent;
  }
  //其他恳求正常跳转
  return NavigationDecision.navigate;
},

flutter和web交互(JavascriptChannel)

flutter和web交互其实也很简单,经过web给出的JavascriptChannel进行交互就能够了,如下所示

设置JavascriptChannel

//JavascriptChannel来进行交互
javascriptChannels: <JavascriptChannel>{
  //参数为Set,能够传入多个JavascriptChannel,依据name作为哈希值
  JavascriptChannel(
    name: 'flutterMessage',
    onMessageReceived: (JavascriptMessage message) {
      print("flutter接纳到了web端发送过来的flutterMessage音讯${message.message}"); //js发送过来的信息
    },
  ),
  JavascriptChannel(
    name: 'flutterOrder',
    onMessageReceived: (JavascriptMessage message) {
      //js发送过来的信息,咱们能够进行处理或许跳转等
      print("flutter接纳到了web端发送过来的flutterOrder音讯${message.message}"); //js发送过来的信息
      _controller?.loadUrl("https:///ios");
    },
  ),
},

运用了 JavascriptMessage 之后,会将 JavascriptChannel里边的 name 挂载到 web 项目window上(以 react 为例),让咱们看看 react 的代码,其他端也差不多,能够运转一下本事例测验

web 端,经过 postMessage 能够传递音讯给 flutter,那边的回调就能接纳到音讯了

function App() {
  const [message, setMessage] = useState('')
  useEffect(() => {
    window.flutterMessage.webReceiveMessage = (message) => {
      setMessage(message)
    }
    return () => {
      delete window.flutterMessage.webReceiveMessage
    }
  }, [])
  return (
    <div className="App">
      <div onClick={() => {
        window.flutterMessage?.postMessage("发送了FlutterMessage音讯给flutter");
      }}>点击发送FlutterMessage音讯</div>
      <div onClick={() => {
        window.flutterOrder?.postMessage("发送了FlutterOrder音讯给flutter");
      }}>点击发送FlutterOrder音讯</div>
      <div>接纳到的message:{message}</div>
    </div>
  );
}

flutter 自动给 web 端发音讯,代码如下

//发送给web端音讯,其间 webReceiveMessage 为 web 给 'name' 增加的办法
_controller?.runJavascript('flutterMessage.webReceiveMessage("收到了没")');

经过上面代码能够看到, 在flutter中设置JavascriptChannel,会将JavascriptChannelname为目标挂载到 webwindow上面

web端:经过 'name' + postMessage 发送音讯给flutter

flutter: 经过 _controller.runJavascript调用 js 脚本,其间脚本为 'name' + web在name下增加的办法,就这样直接调用即可

交互原理便是经过 flutterwebwindow 上挂载的同名目标,经过给该目标增加办法,然后自动调用来完成交互

最终

快来测验一下吧,事例开头也符号了,能够运转一下试试,webflutter 项目都要一同运转才能够哈