[Flutter翻译]InAppWebView:Flutter中WebViews的真正威力

8,235 阅读9分钟

原文地址:medium.com/flutter-com…

原文作者:medium.com/@pichillilo…

发布时间:2020年7月2日-9分钟阅读

Flutter InAppWebView.

什么是flutter_inappwebview ? 这是一个Flutter插件,它允许你将WebView小部件整合到你的Flutter应用中,使用无头WebViews,或者使用In-App浏览器

那么,webview_flutter(官方flutter插件)和flutter_webview_plugin有什么区别呢?

与其他所有WebView插件相比,它的功能很丰富:有很多事件方法选项来控制WebViews。此外,他们并没有很好的关于他们的API的文档,或者说,至少是不完整的。相反,flutter_inappwebview的每一个功能几乎都有文档可查(只要查看pub.dev上的API Reference)。

在这篇文章中,我将介绍人们在flutter_inappwebview官方仓库(issue部分)和StackOverflow上询问的InAppWebView小部件的主要类和一些例子。

主类概述

这是一个插件提供的主要类的列表。

在这篇文章中,我将特别展示InAppWebView小部件,那是最常用/最需要的一个。

InAppWebView是一个和其他Widget一样的Widget!

InAppWebView小部件添加到你的应用程序中是非常简单的。它只是一个像任何其他Flutter小部件一样的小部件。InAppWebView(initialUrl: 'https://github.com/flutter')

注意:要在iOS上使用它,你需要通过在应用程序的Info.plist文件中添加一个布尔属性来选择嵌入式视图预览,其键为io.flutter.embedded_views_preview,值为YES。

这个widget有一组初始属性,你可以用它来初始化WebView。

  • initialUrl: 将被加载的初始URL。
  • initialOptions:将被加载的初始URL; initialOptions:将被使用的初始WebView选项。将要使用的初始WebView选项。
  • gestureRecognizers:指定哪些手势应该被WebView消耗。
  • initialData:初始InAppWebViewInitial数据。将要加载的InAppWebViewInitialData的初始数据,比如一个HTML字符串。
  • initialFile:将被加载的初始资产文件(查看 "加载资产文件夹内的文件 "部分)。
  • initialHeaders: 将要使用的初始头文件。将要使用的初始头文件。
  • contextMenu:上下文菜单,包含自定义菜单项。上下文菜单,包含自定义菜单项。

所有可用的WebView选项列表相当长,例如,您可以使用javascriptEnabled选项启用/禁用JavaScript,或使用cacheEnabled选项启用/禁用缓存。所有选项的完整列表可以在这里找到。

使用InAppWebViewController来控制你的WebView。

相反,为了控制WebView,你有InAppWebViewController类。当WebView准备好使用时,这个控制器由onWebViewCreated回调返回。

通过它,你可以控制你的WebView或者访问它的属性,比如使用getUrl方法访问当前的URL。其他方法,例如,loadUrl加载一个新的URL,postUrl使用POST方法加载一个给定的带有自定义数据的URL,evaluationJavascript评估JavaScript代码到WebView中,并得到评估结果,takeScreenshot拍摄WebView可见视口的截图(PNG格式),getCertificate获取主顶层页面的SSL证书,如果没有证书则为null。你可以使用的所有方法的完整列表相当长,可以在这里找到。

InAppWebView事件

InAppWebView小部件提供了各种事件! 下面是其中的几个事件。

  • onLoadStart:当WebView开始加载一个URL时被触发的事件。
  • onLoadStop:当WebView完成加载一个URL时触发的事件。
  • onLoadHttpError:当WebView主页面收到一个HTTP错误时被触发的事件。
  • onConsoleMessage:当WebView收到JavaScript控制台消息(如console.logconsole.error等)时触发的事件。
  • shouldOverrideUrlLoading:当当前WebView中的URL即将被加载时,给主机应用程序一个控制的机会。
  • onDownloadStart:当WebView识别到一个可下载的文件时发射的事件。
  • onReceivedHttpAuthRequest:当WebView接收到HTTP认证请求时触发的事件。默认行为是取消该请求。
  • onReceivedServerTrustAuthRequest:当WebView需要执行服务器信任认证(证书验证)时被触发的事件。
  • onPrint:当window.print()从JavaScript端被调用时被触发的事件,默认行为是取消请求;onCreateWindow:当WebView需要进行服务器信任验证(证书验证)时被触发的事件。
  • onCreateWindow: 当InAppWebView请求主机应用程序创建一个新窗口时,例如当试图打开一个target="_blank"的链接或当window.open()被JavaScript端调用时,事件被触发。

还有很多很多 我建议查看API参考资料来了解更多细节。至于WebView的选项和方法,所有WebView事件的完整列表相当长,可以在这里找到。

InAppWebView简单示例

这是一个简单的例子,显示了一个InAppWebView小组件,它的当前URL和3个按钮:一个是返回,一个是前进,另一个是重新加载当前页面。

InAppWebView的例子。

这是完整的代码示例。

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';

Future main() async {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(new MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => new _MyAppState();
}

class _MyAppState extends State<MyApp> {

  InAppWebViewController _webViewController;
  String url = "";
  double progress = 0;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('InAppWebView Example'),
        ),
        body: Container(
            child: Column(children: <Widget>[
              Container(
                padding: EdgeInsets.all(20.0),
                child: Text(
                    "CURRENT URL\n${(url.length > 50) ? url.substring(0, 50) + "..." : url}"),
              ),
              Container(
                  padding: EdgeInsets.all(10.0),
                  child: progress < 1.0
                      ? LinearProgressIndicator(value: progress)
                      : Container()),
              Expanded(
                child: Container(
                  margin: const EdgeInsets.all(10.0),
                  decoration:
                  BoxDecoration(border: Border.all(color: Colors.blueAccent)),
                  child: InAppWebView(
                    initialUrl: "https://flutter.dev/",
                    initialOptions: InAppWebViewGroupOptions(
                        crossPlatform: InAppWebViewOptions(
                          debuggingEnabled: true,
                        )
                    ),
                    onWebViewCreated: (InAppWebViewController controller) {
                      _webViewController = controller;
                    },
                    onLoadStart: (InAppWebViewController controller, String url) {
                      setState(() {
                        this.url = url;
                      });
                    },
                    onLoadStop: (InAppWebViewController controller, String url) async {
                      setState(() {
                        this.url = url;
                      });
                    },
                    onProgressChanged: (InAppWebViewController controller, int progress) {
                      setState(() {
                        this.progress = progress / 100;
                      });
                    },
                  ),
                ),
              ),
              ButtonBar(
                alignment: MainAxisAlignment.center,
                children: <Widget>[
                  RaisedButton(
                    child: Icon(Icons.arrow_back),
                    onPressed: () {
                      if (_webViewController != null) {
                        _webViewController.goBack();
                      }
                    },
                  ),
                  RaisedButton(
                    child: Icon(Icons.arrow_forward),
                    onPressed: () {
                      if (_webViewController != null) {
                        _webViewController.goForward();
                      }
                    },
                  ),
                  RaisedButton(
                    child: Icon(Icons.refresh),
                    onPressed: () {
                      if (_webViewController != null) {
                        _webViewController.reload();
                      }
                    },
                  ),
                ],
              ),
            ])),
      ),
    );
  }
}

JavaScript处理程序(通道)

你可以和JavaScript端进行通信,反之亦然。要添加一个JavaScript处理程序,可以使用_webViewController.addJavaScriptHandler方法,在该方法中定义了handlerName和一个callback,当它被JavaScript端调用时,就会被调用。回调可以返回JavaScript端要发送的数据。

而在JavaScript端,要执行回调处理程序并向Flutter发送数据,则需要使用window.flutter_inappwebview.callHandler(handlerName <String>,...args)方法,其中handlerName是一个字符串,代表你调用的处理程序名称,args是你可以向Flutter端发送的可选参数。

为了正确调用window.flutter_inappwebview.callHandler(handlerName <String>, ...args),你需要等待并监听JavaScript事件flutterInAppWebViewPlatformReady。一旦平台(Android或iOS)准备好处理callHandler方法,这个事件就会被派发。

下面是一个例子。

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';

Future main() async {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(new MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => new _MyAppState();
}

class _MyAppState extends State<MyApp> {
  InAppWebViewController _webViewController;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('InAppWebView Example'),
        ),
        body: Container(
            child: Column(children: <Widget>[
              Expanded(
                child:InAppWebView(
                    initialData: InAppWebViewInitialData(
                      data: """
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    </head>
    <body>
        <h1>JavaScript Handlers (Channels) TEST</h1>
        <script>
            window.addEventListener("flutterInAppWebViewPlatformReady", function(event) {
                window.flutter_inappwebview.callHandler('handlerFoo')
                  .then(function(result) {
                    // print to the console the data coming
                    // from the Flutter side.
                    console.log(JSON.stringify(result));
                    
                    window.flutter_inappwebview
                      .callHandler('handlerFooWithArgs', 1, true, ['bar', 5], {foo: 'baz'}, result);
                });
            });
        </script>
    </body>
</html>
                      """
                    ),
                    initialOptions: InAppWebViewGroupOptions(
                        crossPlatform: InAppWebViewOptions(
                          debuggingEnabled: true,
                        )
                    ),
                    onWebViewCreated: (InAppWebViewController controller) {
                      _webViewController = controller;

                      _webViewController.addJavaScriptHandler(handlerName:'handlerFoo', callback: (args) {
                        // return data to JavaScript side!
                        return {
                          'bar': 'bar_value', 'baz': 'baz_value'
                        };
                      });

                      _webViewController.addJavaScriptHandler(handlerName: 'handlerFooWithArgs', callback: (args) {
                        print(args);
                        // it will print: [1, true, [bar, 5], {foo: baz}, {bar: bar_value, baz: baz_value}]
                      });
                    },
                    onConsoleMessage: (controller, consoleMessage) {
                      print(consoleMessage);
                      // it will print: {message: {"bar":"bar_value","baz":"baz_value"}, messageLevel: 1}
                    },
                ),
              ),
            ])),
      ),
    );
  }
}

InAppWebView中的WebRTC

目前,WebRTC只在Android上支持,因为不幸的是,在iOS上WKWebView并没有实现所有的WebRTC API(你可以关注这个问题:#200)。

我将使用appr.tc/ 来展示一个测试WebRTC功能的例子。这是一个基于WebRTC的视频聊天演示应用(github.com/webrtc/appr…

要请求关于摄像头和麦克风的权限,你可以使用 permission_handler 插件。另外,你需要将WebView选项mediaPlaybackRequiresUserGesture设置为false,才能自动播放HTML5音频和视频。

此外,在Android上,你需要实现androidOnPermissionRequest事件(这是一个Android特有的事件),也就是当WebView请求访问特定资源的权限时,会被触发的事件(也就是Android原生WebChromeClient.onPermissionRequest事件)。在这种情况下,该事件用于授予WebRTC API的权限。此外,你还需要在AndroidManifest.xml中添加这些权限。

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.VIDEO_CAPTURE" />
<uses-permission android:name="android.permission.AUDIO_CAPTURE" />

WebRTC的例子。

下面是完整的代码示例。

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:permission_handler/permission_handler.dart';

Future main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await Permission.camera.request();
  await Permission.microphone.request();

  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => new _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: InAppWebViewPage()
    );
  }
}

class InAppWebViewPage extends StatefulWidget {
  @override
  _InAppWebViewPageState createState() => new _InAppWebViewPageState();
}

class _InAppWebViewPageState extends State<InAppWebViewPage> {
  InAppWebViewController _webViewController;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
            title: Text("InAppWebView")
        ),
        body: Container(
            child: Column(children: <Widget>[
              Expanded(
                child: Container(
                  child: InAppWebView(
                      initialUrl: "https://appr.tc/r/704328056",
                      initialOptions: InAppWebViewGroupOptions(
                        crossPlatform: InAppWebViewOptions(
                          mediaPlaybackRequiresUserGesture: false,
                          debuggingEnabled: true,
                        ),
                      ),
                      onWebViewCreated: (InAppWebViewController controller) {
                        _webViewController = controller;
                      },
                      androidOnPermissionRequest: (InAppWebViewController controller, String origin, List<String> resources) async {
                        return PermissionRequestResponse(resources: resources, action: PermissionRequestResponseAction.GRANT);
                      }
                  ),
                ),
              ),
            ]))
    );
  }
}

如何在InAppWebView中启用下载文件的功能

InAppWebView可以识别Android和iOS平台上的可下载文件。为了能够识别可下载文件,你需要设置useOnDownloadStart: true选项,然后你就可以监听onDownloadStart事件。

在Android平台上,你需要在你的AndroidManifest.xml文件里面添加写权限。

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

然后,你需要使用 permission_handler 插件询问权限。相反,为了有效下载你的文件,你可以使用flutter_downloader插件。

这里是一个完整的例子,使用ovh.net/files/(特别是h… 作为URL)来测试下载。

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';

Future main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await FlutterDownloader.initialize(
      debug: true // optional: set false to disable printing logs to console
  );
  await Permission.storage.request();
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => new _MyAppState();
}

class _MyAppState extends State<MyApp> {
  InAppWebViewController _webViewController;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('InAppWebView Example'),
        ),
        body: Container(
            child: Column(children: <Widget>[
              Expanded(
                  child: InAppWebView(
                    initialUrl: "http://ovh.net/files/1Mio.dat",
                    initialOptions: InAppWebViewGroupOptions(
                      crossPlatform: InAppWebViewOptions(
                          debuggingEnabled: true,
                          useOnDownloadStart: true
                      ),
                    ),
                    onWebViewCreated: (InAppWebViewController controller) {
                      _webViewController = controller;
                    },
                    onDownloadStart: (controller, url) async {
                      print("onDownloadStart $url");
                      final taskId = await FlutterDownloader.enqueue(
                        url: url,
                        savedDir: (await getExternalStorageDirectory()).path,
                        showNotification: true, // show download progress in status bar (for Android)
                        openFileFromNotification: true, // click on notification to open downloaded file (for Android)
                      );
                    },
                  ))
            ])),
      ),
    );
  }
}

正如你所看到的,我也使用path_provider插件来获取我想保存文件的文件夹。

允许使用自签名的SSL证书

要允许自签名的SSL证书,你可以使用onReceivedServerTrustAuthRequest事件,然后简单地返回继续请求。

onReceivedServerTrustAuthRequest: (controller, challenge) async {
  return ServerTrustAuthResponse(action: ServerTrustAuthResponseAction.PROCEED);
},

如何管理用target="_blank "或 "window.open "打开的弹出窗口。

当用户点击target="_blank "的链接或通过使用window.open的JavaScript代码来管理弹出窗口时,你可以使用onCreateWindow事件。在Android上,为了能够允许这个事件,你需要将supportMultipleWindows选项设置为true。另外,为了能够允许使用JavaScript,你需要将javaScriptCanOpenWindowsAutomatically设置为true。

如果你想管理这些请求,你应该从这个事件中返回true,否则,这个事件的默认实现什么也不做,因此返回false

CreateWindowRequest代表导航请求,它包含一个windowId,可以用来创建,例如,一个新的InAppWebView实例。这个windowId被本地代码用来映射该请求和用于管理该请求的WebView。 另外,CreateWindowRequest包含了请求的url(在Android上,如果弹出窗口是用window.open的JavaScript打开的,它将是null),但是如果你需要维护Window JavaScript对象引用(用window.open方法创建的),例如,调用window.close方法,那么你应该用windowId创建新的WebView,而不使用url。 下面是一个简单的例子,当用户点击链接时,会显示一个AlertDialog

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';

Future main() async {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(new MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => new _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: InAppWebViewPage()
    );
  }
}

class InAppWebViewPage extends StatefulWidget {
  @override
  _InAppWebViewPageState createState() => new _InAppWebViewPageState();
}

class _InAppWebViewPageState extends State<InAppWebViewPage> {
  InAppWebViewController _webViewController;
  InAppWebViewController _webViewPopupController;

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('InAppWebView Example'),
        ),
        body: SafeArea(
          child: Container(
              child: InAppWebView(
                initialData: InAppWebViewInitialData(
                    data: """
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Flutter InAppWebView</title>
</head>
<body>
  <a style="margin: 50px; background: #333; color: #fff; font-weight: bold; font-size: 20px; padding: 15px; display: block;"
    href="https://github.com/flutter"
    target="_blank">
    Click here to open https://github.com/flutter in a popup!
  </a>
</body>
</html>
"""
                ),
                initialOptions: InAppWebViewGroupOptions(
                    crossPlatform: InAppWebViewOptions(
                      debuggingEnabled: true,
                      // set this to true if you are using window.open to open a new window with JavaScript
                      javaScriptCanOpenWindowsAutomatically: true
                    ),
                    android: AndroidInAppWebViewOptions(
                      // on Android you need to set supportMultipleWindows to true,
                      // otherwise the onCreateWindow event won't be called
                      supportMultipleWindows: true
                    )
                ),
                onWebViewCreated: (InAppWebViewController controller) {
                  _webViewController = controller;
                },
                onCreateWindow: (controller, createWindowRequest) async {

                  print("onCreateWindow");

                  showDialog(
                    context: context,
                    builder: (context) {
                      return AlertDialog(
                        content: Container(
                          width: MediaQuery.of(context).size.width,
                          height: 400,
                          child: InAppWebView(
                            // Setting the windowId property is important here!
                            windowId: createWindowRequest.windowId,
                            initialOptions: InAppWebViewGroupOptions(
                                crossPlatform: InAppWebViewOptions(
                                    debuggingEnabled: true,
                                ),
                            ),
                            onWebViewCreated: (InAppWebViewController controller) {
                              _webViewPopupController = controller;
                            },
                            onLoadStart: (InAppWebViewController controller, String url) {
                              print("onLoadStart popup $url");
                            },
                            onLoadStop: (InAppWebViewController controller, String url) {
                              print("onLoadStop popup $url");
                            },
                          ),
                        ),
                      );
                    },
                  );

                  return true;
                },
              ),
            ),
          ),
      ),
    );
  }
}

onCreateWindow事件 - 弹出窗口示例。

管理平台URL,如whatsapp:、fb:、tel:、mailto:等。

一般情况下,WebView不知道如何管理whatsapp:tel:fb:协议/scheme: ,tel:或fb:协议/scheme。为了捕获这些自定义协议/scheme的请求,你可以使用shouldOverrideUrlLoading事件(你需要用useShouldOverrideUrlLoading: true选项启用它)。 这样你就可以取消对WebView的请求,而是打开App,例如,使用url_launcher插件。

initialOptions: InAppWebViewGroupOptions(
  crossPlatform: InAppWebViewOptions(
      debuggingEnabled: true,
      useShouldOverrideUrlLoading: true
  ),
),
shouldOverrideUrlLoading: (controller, request) async {
  var url = request.url;
  var uri = Uri.parse(url);

  if (!["http", "https", "file",
    "chrome", "data", "javascript",
    "about"].contains(uri.scheme)) {
    if (await canLaunch(url)) {
      // Launch the App
      await launch(
        url,
      );
      // and cancel the request
      return ShouldOverrideUrlLoadingAction.CANCEL;
    }
  }

  return ShouldOverrideUrlLoadingAction.ALLOW;
},

管理WebView Cookies

要管理WebView cookie,可以使用CookieManager类,它实现了一个单人对象(共享实例)。在 Android 上,它是使用 CookieManager 类实现的。在iOS上,它是使用WKHTTPCookieStore类实现的。

下面是一个如何设置cookie的例子。

CookieManager _cookieManager = CookieManager.instance();

final expiresDate =
    DateTime.now().add(Duration(days: 3)).millisecondsSinceEpoch;
_cookieManager.setCookie(
  url: "https://flutter.dev/",
  name: "session",
  value: "54th5hfdcfg34",
  domain: ".flutter.dev",
  expiresDate: expiresDate,
  isSecure: true,
);

自定义上下文菜单

您可以自定义WebView的上下文菜单,添加自定义菜单项,和/或隐藏默认的系统菜单项。对于每个自定义菜单项,你可以声明一个回调action,当用户点击它时,就会被调用。举个例子,我将添加一个名为Special的自定义菜单项,并定义一个回调动作,在用户选择文本时向用户显示一个JavaScript window.alert

自定义菜单项的例子。

这里是完整的代码示例。

import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';

Future main() async {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => new _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: InAppWebViewPage()
    );
  }
}

class InAppWebViewPage extends StatefulWidget {
  @override
  _InAppWebViewPageState createState() => new _InAppWebViewPageState();
}

class _InAppWebViewPageState extends State<InAppWebViewPage> {
  InAppWebViewController _webViewController;
  ContextMenu contextMenu;

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

    contextMenu = ContextMenu(
        menuItems: [
          ContextMenuItem(androidId: 1, iosId: "1", title: "Special", action: () async {
            print("Menu item Special clicked!");
            var selectedText = await _webViewController.getSelectedText();
            await _webViewController.clearFocus();
            await _webViewController.evaluateJavascript(source: "window.alert('You have selected: $selectedText')");
          })
        ],
        options: ContextMenuOptions(
            hideDefaultSystemContextMenuItems: false
        ),
        onCreateContextMenu: (hitTestResult) async {
          print("onCreateContextMenu");
          print(hitTestResult.extra);
          print(await _webViewController.getSelectedText());
        },
        onHideContextMenu: () {
          print("onHideContextMenu");
        },
        onContextMenuActionItemClicked: (contextMenuItemClicked) async {
          var id = (Platform.isAndroid) ? contextMenuItemClicked.androidId : contextMenuItemClicked.iosId;
          print("onContextMenuActionItemClicked: " + id.toString() + " " + contextMenuItemClicked.title);
        }
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
            title: Text("InAppWebView")
        ),
        body: Container(
            child: Column(children: <Widget>[
              Expanded(
                child: Container(
                  child: InAppWebView(
                      initialUrl: "https://github.com/flutter",
                      contextMenu: contextMenu,
                      initialOptions: InAppWebViewGroupOptions(
                        crossPlatform: InAppWebViewOptions(
                          debuggingEnabled: true,
                        ),
                      ),
                      onWebViewCreated: (InAppWebViewController controller) {
                        _webViewController = controller;
                      },
                  ),
                ),
              ),
            ]))
    );
  }
}

结束语

在这篇文章中,我对flutter_inappwebview插件做了一点介绍,特别是关于InAppWebView小部件的介绍。这个插件正在持续开发中(在写这篇文章的时候,最新的版本是4.0.0),我建议你查看API Reference来了解所有的功能。对于任何新的功能请求/bug修复,你可以使用版本库的问题部分。

下一篇文章将介绍如何使用这个插件实现一个全功能浏览器在Flutter中使用WebViews创建一个全功能浏览器

今天的内容就到这里了! 希望它为你的Flutter应用打开了新的用例。

www.twitter.com/FlutterComm


通过www.DeepL.com/Translator(免费版)翻译