在阅读前需要了解

android Webview的使用以及和js的交互, 见网页

flutter的plugin开发流程, 见网页

flutter中使用原生视图的流程, 见网页

这是官网提供的主要类, 本文只讲第一个InAppWebView 以及 js交互

下图是插件中InAppWebView类在dart端和android端的关系 (InAppWebView后面简称为IAwebview) 下图是我自己画的 下图来自参考文章1

dart端主要类解读

这里有官方提供的使用示例

// dart语言, csdn没有高亮

import 'dart:async';

import 'dart:io';

import 'package:flutter/material.dart';

import 'package:flutter_inappwebview/flutter_inappwebview.dart';

import 'package:url_launcher/url_launcher.dart';

Future main() async {

WidgetsFlutterBinding.ensureInitialized();

if (Platform.isAndroid) {

await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);

}

runApp(MaterialApp(

home: new MyApp()

));

}

class MyApp extends StatefulWidget {

@override

_MyAppState createState() => new _MyAppState();

}

class _MyAppState extends State {

final GlobalKey webViewKey = GlobalKey();

InAppWebViewController? webViewController;

InAppWebViewGroupOptions options = InAppWebViewGroupOptions(

crossPlatform: InAppWebViewOptions(

useShouldOverrideUrlLoading: true,

mediaPlaybackRequiresUserGesture: false,

),

android: AndroidInAppWebViewOptions(

useHybridComposition: true,

),

ios: IOSInAppWebViewOptions(

allowsInlineMediaPlayback: true,

));

late PullToRefreshController pullToRefreshController;

String url = "";

double progress = 0;

final urlController = TextEditingController();

@override

void initState() {

super.initState();

pullToRefreshController = PullToRefreshController(

options: PullToRefreshOptions(

color: Colors.blue,

),

onRefresh: () async {

if (Platform.isAndroid) {

webViewController?.reload();

} else if (Platform.isIOS) {

webViewController?.loadUrl(

urlRequest: URLRequest(url: await webViewController?.getUrl()));

}

},

);

}

@override

void dispose() {

super.dispose();

}

@override

Widget build(BuildContext context) {

return Scaffold(

appBar: AppBar(title: Text("Official InAppWebView website")),

body: SafeArea(

child: Column(children: [

TextField(

decoration: InputDecoration(

prefixIcon: Icon(Icons.search)

),

controller: urlController,

keyboardType: TextInputType.url,

onSubmitted: (value) {

var url = Uri.parse(value);

if (url.scheme.isEmpty) {

url = Uri.parse("https://www.google.com/search?q=" + value);

}

webViewController?.loadUrl(

urlRequest: URLRequest(url: url));

},

),

Expanded(

child: Stack(

children: [

InAppWebView(

key: webViewKey,

initialUrlRequest:

URLRequest(url: Uri.parse("https://inappwebview.dev/")),

initialOptions: options,

pullToRefreshController: pullToRefreshController,

onWebViewCreated: (controller) {

webViewController = controller;

},

onLoadStart: (controller, url) {

setState(() {

this.url = url.toString();

urlController.text = this.url;

});

},

androidOnPermissionRequest: (controller, origin, resources) async {

return PermissionRequestResponse(

resources: resources,

action: PermissionRequestResponseAction.GRANT);

},

shouldOverrideUrlLoading: (controller, navigationAction) async {

var uri = navigationAction.request.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 NavigationActionPolicy.CANCEL;

}

}

return NavigationActionPolicy.ALLOW;

},

onLoadStop: (controller, url) async {

pullToRefreshController.endRefreshing();

setState(() {

this.url = url.toString();

urlController.text = this.url;

});

},

onLoadError: (controller, url, code, message) {

pullToRefreshController.endRefreshing();

},

onProgressChanged: (controller, progress) {

if (progress == 100) {

pullToRefreshController.endRefreshing();

}

setState(() {

this.progress = progress / 100;

urlController.text = this.url;

});

},

onUpdateVisitedHistory: (controller, url, androidIsReload) {

setState(() {

this.url = url.toString();

urlController.text = this.url;

});

},

onConsoleMessage: (controller, consoleMessage) {

print(consoleMessage);

},

),

progress < 1.0

? LinearProgressIndicator(value: progress)

: Container(),

],

),

),

ButtonBar(

alignment: MainAxisAlignment.center,

children: [

ElevatedButton(

child: Icon(Icons.arrow_back),

onPressed: () {

webViewController?.goBack();

},

),

ElevatedButton(

child: Icon(Icons.arrow_forward),

onPressed: () {

webViewController?.goForward();

},

),

ElevatedButton(

child: Icon(Icons.refresh),

onPressed: () {

webViewController?.reload();

},

),

],

),

]))

);

}

}

InAppWebView类

实际上是将原生的android视图嵌入到flutter中 即将原生的webview封装成widget Flutter 支持两种集成模式:虚拟显示模式 (Virtual displays) 和混合集成模式 (Hybrid composition) 。

InAppWebView默认使用第一种 也可以设置widget的参数来指定使用哪一种 源码判断模式的代码: useHybridComposition =widget.initialOptions?.android.useHybridComposition ?? false;

其构造函数中可以传入很多回调函数, 并在其中获取到类实例对应的InAppWebViewController 实例

InAppWebViewController 类

此类可以控制 InAppWebView

相当于是android原生webview的方法和Setings, WebViewClient 以及WebChromeClient的方法加起来

实际上使用此类时其方法还是通过方法通道 去调用原生的extends WebView的InAppWebView的方法和这三个类的方法 同时也将原生webview的周期回调函数通过方法通道接收和处理, 处理中调用使用者传入的函数

android端主要类解读

插件类的源码入口就在继承了FlutterPlugin的类中

public class InAppWebViewFlutterPlugin implements FlutterPlugin, ActivityAware {

@Override

public void onAttachedToEngine(FlutterPluginBinding binding) {

this.flutterAssets = binding.getFlutterAssets();

// 在其中注册webviewFactory

onAttachedToEngine(

binding.getApplicationContext(), binding.getBinaryMessenger(), this.activity, binding.getPlatformViewRegistry(), null);

}

//-----next class--------------------

// webviewFactory的create()返回一个flutterWebView对象

public class FlutterWebView implements PlatformWebView {

// 构造函数中初始化了 channel

// 此channel在dart接口中被in_app_webview_controller使用

channel = new MethodChannel(plugin.messenger, "com.pichillilorenzo/flutter_inappwebview_" + id);

// 设置其方法调用的handler

methodCallDelegate = new InAppWebViewMethodHandler(webView);

channel.setMethodCallHandler(methodCallDelegate);

//-----next class--------------------

// ❗ ❗ ❗ ❗ ❗ ❗ methodChannel的handler, 重要代码

// 此handler重写了onMethodCall(), 有600行代码, 100个case来处理来自flutter端不同的方法, 通过这些方法对webview进行控制

public class InAppWebViewMethodHandler implements MethodChannel.MethodCallHandler {

// flutterWebView的构造函数中初始化了一个InAppWebView, 调用其prepare()

// InAppWebView 继承 InputAwareWebview, InputAwareWebview 继承原生的webview

final public class InAppWebView extends InputAwareWebView implements InAppWebViewInterface {

// prepare()中设置了webview的webChromeClient, webViewClient, websettings

// 其中还设置了 userContentController

public void prepare() {

httpClient = new OkHttpClient().newBuilder().build();

// 这里添加了一个java对象映射到webview中

javaScriptBridgeInterface = new JavaScriptBridgeInterface(this);

/*

对象在js中的名字 JAVASCRIPT_BRIDGE_NAME = "flutter_inappwebview";

对象的关键方法 : public void _callHandler(final String handlerName, final String _callHandlerID, final String args)

虽然不返回值, 但是在内部会根据handlername 去调用不同的函数或者通过方法通道传给flutter去hanle

然后callAsyncJavaScriptCallbacks()返回需要返回的值

*/

addJavascriptInterface(javaScriptBridgeInterface, JavaScriptBridgeJS.JAVASCRIPT_BRIDGE_NAME);

inAppWebViewChromeClient = new InAppWebViewChromeClient(plugin, channel, inAppBrowserDelegate);

setWebChromeClient(inAppWebViewChromeClient);

inAppWebViewClient = new InAppWebViewClient(channel, inAppBrowserDelegate);

setWebViewClient(inAppWebViewClient);

具体功能实现的源码

IAwebview如何加载url

// 仍然是通过InAppWebViewController控制

InAppWebViewController? webViewController;

// ----分割线-------------

webViewController?.loadUrl(urlRequest: urlRequest);

// ----分割线-------------

Future loadUrl(

{

required URLRequest urlRequest,

@Deprecated('Use `allowingReadAccessTo` instead')

Uri? iosAllowingReadAccessTo,

Uri? allowingReadAccessTo}) async {

Map args = {

};

await _channel.invokeMethod('loadUrl', args);

// 原生端methodHandler的方法处理

case "loadUrl":

if (webView != null) {

Map urlRequest = (Map) call.argument("urlRequest");

webView.loadUrl(URLRequest.fromMap(urlRequest));

}

// IAwebview根据urlRequest的不同调用原生webview不同的loadurl()

public void loadUrl(URLRequest urlRequest) {

String url = urlRequest.getUrl();

String method = urlRequest.getMethod();

// request的方法为post

if (method != null && method.equals("POST")) {

byte[] postData = urlRequest.getBody();

postUrl(url, postData);

return;

}

//

Map headers = urlRequest.getHeaders();

if (headers != null) {

loadUrl(url, headers);

return;

精彩文章

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: