Flutter 外掛 webview_flutter 使用指北

darkmirrors發表於2019-09-12

現狀

目前主流有兩個外掛,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
  • 獲取裝置的網路狀態

參考文章

相關文章