日常咱们在开发项目时,为了项目快速的开发和迭代,难免会用到H5页面。运用Flutter进行项目开发时,也相同免不了要加载H5页面,在移动开发中翻开H5页面需求运用WebView组件。一起,为了和H5页面进行数据交换,有时分还需求凭仗JSBridge来结束客户端与H5之间的通讯。

添加 webview_flutter 组件

在项目的pubspec.yaml文件中添加依靠:webview_flutter: ^3.0.0,然后实行pub get

  • 由于加载WebView需求运用网络,所以还需求在android中添加网络权限。翻开目录android/app/src/main/AndroidManifest.xml,然后添加如下代码即可。
<uses-permission android:name="android.permission.INTERNET"/>
  • 由于iOS在9.0版别默许敞开了Https,所以要工作Http的网页,还需求在ios/Runner/Info.plist文件中添加如下代码。
<key>io.flutter.embedded_views_preview</key>
<string>YES</string>

webview_flutter 组件的结构方法的简单介绍

WebView({
    Key key,
    this.onWebViewCreated,             //WebView创建结束之后的回调
    this.initialUrl,                               // 初始化 URL
    this.javascriptMode = JavascriptMode.disabled,    //JS实行形式,默许是不调用
    this.javascriptChannels,             // JS可以调用Flutter 的通道
    this.navigationDelegate,            // 路由托付,可以运用它实行拦截操作
    this.gestureRecognizers,          // 手势监听相关
    this.onPageStarted,                 //初步加载页面回调
    this.onPageFinished,              // 页面加载结束的回调
    this.onWebResourceError,     //资源加载失利回调
    this.debuggingEnabled = false,
    this.gestureNavigationEnabled = false,
    this.userAgent,
    this.initialMediaPlaybackPolicy =
        AutoMediaPlaybackPolicy.require_user_action_for_all_media_types,
  })

运用 Webview 加载网页时,许多时分需求与JS进行交互,即JS调用FlutterFlutter调用JS

检查官方文档,发现供给的能力,在实践开发中可以参阅这些功用。包括:网络央求、Cookies相关、缓存相关、加载html、加载失利页面等等。

Future<void> _onShowUserAgent(
      WebViewController controller, BuildContext context) async {
    // Send a message with the user agent string to the Toaster JavaScript channel we registered
    // with the WebView.
    await controller.runJavascript(
        'Toaster.postMessage("User Agent: " + navigator.userAgent);');
  }
  Future<void> _onListCookies(
      WebViewController controller, BuildContext context) async {
    final String cookies =
        await controller.runJavascriptReturningResult('document.cookie');
    // ignore: deprecated_member_use
    Scaffold.of(context).showSnackBar(SnackBar(
      content: Column(
        mainAxisAlignment: MainAxisAlignment.end,
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          const Text('Cookies:'),
          _getCookieList(cookies),
        ],
      ),
    ));
  }
  Future<void> _onAddToCache(
      WebViewController controller, BuildContext context) async {
    await controller.runJavascript(
        'caches.open("test_caches_entry"); localStorage["test_localStorage"] = "dummy_entry";');
    // ignore: deprecated_member_use
    Scaffold.of(context).showSnackBar(const SnackBar(
      content: Text('Added a test entry to cache.'),
    ));
  }
  Future<void> _onListCache(
      WebViewController controller, BuildContext context) async {
    await controller.runJavascript('caches.keys()'
        '.then((cacheKeys) => JSON.stringify({"cacheKeys" : cacheKeys, "localStorage" : localStorage}))'
        '.then((caches) => Toaster.postMessage(caches))');
  }
  Future<void> _onClearCache(
      WebViewController controller, BuildContext context) async {
    await controller.clearCache();
    // ignore: deprecated_member_use
    Scaffold.of(context).showSnackBar(const SnackBar(
      content: Text('Cache cleared.'),
    ));
  }
  Future<void> _onClearCookies(BuildContext context) async {
    final bool hadCookies = await cookieManager.clearCookies();
    String message = 'There were cookies. Now, they are gone!';
    if (!hadCookies) {
      message = 'There are no cookies.';
    }
    // ignore: deprecated_member_use
    Scaffold.of(context).showSnackBar(SnackBar(
      content: Text(message),
    ));
  }
  Future<void> _onNavigationDelegateExample(
      WebViewController controller, BuildContext context) async {
    final String contentBase64 =
        base64Encode(const Utf8Encoder().convert(kNavigationExamplePage));
    await controller.loadUrl('data:text/html;base64,$contentBase64');
  }
  Future<void> _onSetCookie(
      WebViewController controller, BuildContext context) async {
    await CookieManager().setCookie(
      const WebViewCookie(
          name: 'foo', value: 'bar', domain: 'httpbin.org', path: '/anything'),
    );
    await controller.loadUrl('https://httpbin.org/anything');
  }
  Future<void> _onDoPostRequest(
      WebViewController controller, BuildContext context) async {
    final WebViewRequest request = WebViewRequest(
      uri: Uri.parse('https://httpbin.org/post'),
      method: WebViewRequestMethod.post,
      headers: <String, String>{'foo': 'bar', 'Content-Type': 'text/plain'},
      body: Uint8List.fromList('Test Body'.codeUnits),
    );
    await controller.loadRequest(request);
  }
  Future<void> _onLoadLocalFileExample(
      WebViewController controller, BuildContext context) async {
    final String pathToIndex = await _prepareLocalFile();
    await controller.loadFile(pathToIndex);
  }
  Future<void> _onLoadFlutterAssetExample(
      WebViewController controller, BuildContext context) async {
    await controller.loadFlutterAsset('assets/www/index.html');
  }
  Future<void> _onLoadHtmlStringExample(
      WebViewController controller, BuildContext context) async {
    await controller.loadHtmlString(kLocalExamplePage);
  }
  Future<void> _onTransparentBackground(
      WebViewController controller, BuildContext context) async {
    await controller.loadHtmlString(kTransparentBackgroundPage);
  }
  Widget _getCookieList(String cookies) {
    if (cookies == null || cookies == '""') {
      return Container();
    }
    final List<String> cookieList = cookies.split(';');
    final Iterable<Text> cookieWidgets =
        cookieList.map((String cookie) => Text(cookie));
    return Column(
      mainAxisAlignment: MainAxisAlignment.end,
      mainAxisSize: MainAxisSize.min,
      children: cookieWidgets.toList(),
    );
  }
  static Future<String> _prepareLocalFile() async {
    final String tmpDir = (await getTemporaryDirectory()).path;
    final File indexFile = File(
        <String>{tmpDir, 'www', 'index.html'}.join(Platform.pathSeparator));
    await indexFile.create(recursive: true);
    await indexFile.writeAsString(kLocalExamplePage);
    return indexFile.path;
  }

JS调用Flutter

navigationDelegate 方法结束

这种方法结束的原理主要是加载网页的时分进行拦截

下面举例结束的代码

js代码:

document.location = "js://webview?name=candy";

flutter 端代码:

navigationDelegate: (NavigationRequest request) {
  if(request.url.startsWith("js://webview")) {
    print("初步处理 ${request.url}");
    return NavigationDecision.prevent;
  }
  return NavigationDecision.navigate;
},

这儿的 NavigationDecision.prevent标明阻挠路由替换,NavigationDecision.navigate标明答应路由替换。

javascriptChannels 方法结束

js代码:

<button onclick="callFlutter()">callFlutter</button>
function callFlutter(){
   Toast.postMessage("js call flutter");  
}

flutter 端代码:

WebView(
javascriptChannels: <JavascriptChannel>[
_shareJavascriptChannel(context),
].toSet(),
)
JavascriptChannel _shareJavascriptChannel(BuildContext context) {
  return JavascriptChannel(
      name: 'share',
      onMessageReceived: (JavascriptMessage message) {
        print("参数: ${message.message}");
        showToast(message.message);
      });
}

Flutter 调用 JS

js代码:

function callJS(message){
document.getElementById("p1").style.visibility = message;
}

Flutter 代码

Future<void> evaluateJavascript() async {
  print('evaluateJavascript');
  _controller.runJavascript('callJS('visible');');
}

加载本地 html

Flutter-webview的运用和JS交互

Flutter-webview的运用和JS交互

html 代码

<!DOCTYPE html>
<html>
<body>
<style>*{font-size:50px;}</style>
<button onclick="callFlutter()">callFlutter</button>
<p id="p1" style="visibility:hidden;">
Flutter代码调用了JS方法.
</p>
<script src="http://code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="http://cdn.amazeui.org/amazeui/2.5.0/js/amazeui.min.js"></script>
<script type="text/javascript">
function callJS(message){
document.getElementById("p1").style.visibility = message;
}
</script>
<script type="text/javascript">
function callFlutter(){
Toaster.postMessage('js call flutter');
}
</script>
</body>
</html>
Future<void> _loadHtmlFromAsset() async {
  String html = 'assets/static/test.html';
  final String path = await rootBundle.loadString(html);
  _controller.loadUrl(Uri.dataFromString(path,
      mimeType: 'text/html', encoding: Encoding.getByName('utf-8'))
      .toString());
}

完好结束代码

import 'dart:convert';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'package:webview_flutter/webview_flutter.dart';
class WebViewPage extends StatefulWidget {
  @override
  _WebViewPageState createState() => _WebViewPageState();
}
class _WebViewPageState extends State<WebViewPage> {
  late WebViewController _controller;
  String _title = "webview";
  //加载Html
  Future<void> _loadHtmlFromAsset() async {
    String html = 'assets/static/test.html';
    final String path = await rootBundle.loadString(html);
    _controller.loadUrl(Uri.dataFromString(path,
        mimeType: 'text/html', encoding: Encoding.getByName('utf-8'))
        .toString());
  }
  @override
  Widget build(BuildContext context) {
    return CupertinoPageScaffold(
      navigationBar: CupertinoNavigationBar(
        middle: Text("$_title"),
      ),
      child: SafeArea(
        child: WebView(
          //initialUrl: "https://flutterchina.club/",
          //JS实行形式 是否答应JS实行
          javascriptMode: JavascriptMode.unrestricted,
          onWebViewCreated: (controller) {
            _controller = controller;
            _loadHtmlFromAsset();
          },
          onPageFinished: (url) async{
           //调用JS方法,获取页面的标题
            String title = await _controller.runJavascriptReturningResult('document.title');
            setState(() {
              _title = title;
            });
            evaluateJavascript();
          },
          navigationDelegate: (NavigationRequest request) {
            if(request.url.startsWith("js://webview")) {
              print("初步处理 ${request.url}");
              return NavigationDecision.prevent;
            }
            return NavigationDecision.navigate;
          },
          javascriptChannels: <JavascriptChannel>{
            JavascriptChannel(
                name: "share",
                onMessageReceived: (JavascriptMessage message) {
                  print("参数: ${message.message}");
                  //实践运用中要通过map通过key获取
                  String callbackname = message.message;
                  String data = "收到音讯调用了";
                  String script = "$callbackname($data)";
                  _controller.runJavascript(script);
                }
            ),
          },
        ),
      ),
    );
  }
  Future<void> evaluateJavascript() async {
    print('evaluateJavascript');
    //这个是结束了Flutter操控了H5页面文本的闪现
    _controller.runJavascript('callJS('visible');');
  }
}

加载网页

给这个属性赋值就可以了

initialUrl: 'https://flutterchina.club/',

页面是否可以回退

Future<bool> _goBack(BuildContext context) async {
  if (_controller != null && await _controller.canGoBack()) {
    _controller.goBack();
    return false;
  }
  return true;
}

官方结束了更多的功用:pub.dev/packages/we…

以上就讲解了 webView 的网页加载、JS交互、网络央求、Cookies相关、缓存相关、加载html、加载失利页面等功用。