赞
踩
在阅读前需要了解
这是官网提供的主要类, 本文只讲第一个InAppWebView 以及 js交互
下图是插件中InAppWebView类在dart端和android端的关系
(InAppWebView后面简称为IAwebview)
下图是我自己画的
下图来自参考文章1
这里有官方提供的使用示例
// 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<MyApp> { 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: <Widget>[ 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: <Widget>[ 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(); }, ), ], ), ])) ); } }
实际上是将原生的android视图嵌入到flutter中
即将原生的webview封装成widget
Flutter 支持两种集成模式:虚拟显示模式 (Virtual displays) 和混合集成模式 (Hybrid composition) 。
InAppWebView默认使用第一种
也可以设置widget的参数来指定使用哪一种
源码判断模式的代码:useHybridComposition =widget.initialOptions?.android.useHybridComposition ?? false;
其构造函数中可以传入很多回调函数, 并在其中获取到类实例对应的InAppWebViewController 实例
此类可以控制 InAppWebView
相当于是android原生webview的方法和Setings, WebViewClient 以及WebChromeClient的方法加起来
实际上使用此类时其方法还是通过方法通道
去调用原生的extends WebView的InAppWebView的方法和这三个类的方法
同时也将原生webview的周期回调函数通过方法通道接收和处理, 处理中调用使用者传入的函数
插件类的源码入口就在继承了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);
// 仍然是通过InAppWebViewController控制 InAppWebViewController? webViewController; // ----分割线------------- webViewController?.loadUrl(urlRequest: urlRequest); // ----分割线------------- Future<void> loadUrl( { required URLRequest urlRequest, @Deprecated('Use `allowingReadAccessTo` instead') Uri? iosAllowingReadAccessTo, Uri? allowingReadAccessTo}) async { Map<String, dynamic> args = <String, dynamic>{ }; await _channel.invokeMethod('loadUrl', args); // 原生端methodHandler的方法处理 case "loadUrl": if (webView != null) { Map<String, Object> urlRequest = (Map<String, Object>) 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<String, String> headers = urlRequest.getHeaders(); if (headers != null) { loadUrl(url, headers); return;
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。