WebView的使用
12147初始化
//定义webView//代码来源:/jianghuAppBrowser/jianghuBrowser/lib/common/RenderKey.dartstatic ValueNotifier<WebViewWidget?> webView = ValueNotifier(null);//初始化webView//代码来源:/jianghuAppBrowser/jianghuBrowser/lib/layout/JianghuConfigHandler.dartFuture<void> initWebView({Function? onWebViewCreated}) async {print("初始化 initWebView");if (RenderKey.controller.value != null) {if (onWebViewCreated != null) {onWebViewCreated();}} else {RenderKey.webView.value = WebViewWidget(onProgress: (progress) {RenderKey.loadingLabel.value = '正在加载$progress%';},onWebViewCreated: () {if (onWebViewCreated != null) {onWebViewCreated();}},onPageLoadEnd: () async {},);}}//监听webView,如果webView已经初始化了,则渲染。//代码来源:/jianghuAppBrowser/jianghuBrowser/lib/layout/JianghuLayout.dartreturn ValueListenableBuilder(valueListenable: RenderKey.webView,builder: (context, WebViewWidget? webView, _) {return webView ?? Container();},);
配置
基本配置
//代码来源:/jianghuAppBrowser/jianghuBrowser/lib/layout/WebViewWidget.dartwebViewWidget(context) {return ValueListenableBuilder(valueListenable: Constants.appId,builder: (context, appId, _) {print("=== appId ValueListenableBuilder: $appId");return WebView(javascriptMode: JavascriptMode.unrestricted,appId: appId,allowFileAccess: true,initialMediaPlaybackPolicy: AutoMediaPlaybackPolicy.always_allow,initialCookies: AuthUtil.cookieName == null ? [] : [WebViewCookie(name: AuthUtil.cookieName!, value: AuthUtil.cookieValue!, domain: AuthUtil.cookieDomain!)],javascriptChannels: <JavascriptChannel>{WebViewHandler().messagePost(context),},gestureNavigationEnabled: true,debuggingEnabled: true,);},);}
- javascriptMode
用于控制WebView是否启用JavaScript执行。这个参数的默认值为JavascriptMode.disabled,表示不允许WebView执行JavaScript代码。
启用JavaScript可以使我们在WebView中执行动态的Web内容,例如更新页面元素、发送异步请求等。当我们需要在WebView中执行JavaScript代码时,我们需要将javascriptMode参数设置为以下三个选项之一:
- JavascriptMode.disabled:禁用JavaScript执行。
- JavascriptMode.unrestricted:允许JavaScript执行。这将允许WebView执行来自任何源的JavaScript代码,可能会导致安全问题。
- JavascriptMode.restricted:允许JavaScript执行,但有一些限制。例如,WebView将只允许JavaScript执行来自WebView的源代码。
需要注意的是,执行JavaScript可能会导致安全问题,因此我们建议在启用JavaScript时保持警觉,并尽可能避免在WebView中加载来自不可信源的内容。
- debuggingEnabled:
用于启用或禁用WebView的调试功能。这个参数的默认值为false,表示禁用调试功能。
启用调试功能可以帮助开发者快速识别和解决WebView中的问题。例如,在WebView中调试JavaScript代码时,我们可以利用浏览器的调试器来定位错误和调试代码。通过启用debuggingEnabled参数,我们可以在WebView中使用类似的调试工具来提高开发效率。
可以使用chrome调试功能,chrome://inspect/#devices
需要注意的是,启用调试模式可能会在WebView中增加一些性能开销,因此我们建议只在必要时启用该模式,并在生产环境中禁用调试模式。
- gestureNavigationEnabled:
用于启用或禁用WebView的手势导航功能。这个参数的默认值为false,表示手势导航被禁用。
手势导航是指在使用WebView时,通过手势操作可以实现页面的前进、后退和刷新功能。例如,在iOS设备上,用户可以使用滑动手势操作来实现页面的前进和后退。这种手势导航功能能够提供更流畅的用户体验,使用户可以更方便地控制WebView的行为。
当gestureNavigationEnabled参数被设置为true时,手势导航功能将启用。
需要注意的是,根据不同平台和WebView的版本,手势导航的实现方式可能略有不同。因此,在使用手势导航功能时,我们建议进行适当的测试和验证。
- initialCookies:
用于在WebView中设置初始cookie。initialCookies提供了一种更方便的方法来向WebView中传递cookie,而不需要在WebView中设置JavaScript bridge来进行cookie管理。这是因为initialCookies参数是一个用于设置初始cookie的List对象,其中每个元素都是一个字符串,该字符串表示一个Cookie的键值对。
以下是一个使用initialCookies的Mock数据:
final List<String> initialCookies = ['session_id=abc123','auth_token=xyz987','user_id=12345',];
在构造函数中添加initialCookies参数。在使用WebView时,我们传递initialCookies作为参数,以便在WebView中设置初始cookie。当我们在WebView中加载网页时,初始化的cookie将被自动添加到HTTPHeader中。
- initialMediaPlaybackPolicy:
用于控制WebView加载媒体资源(例如视频或音频)时的自动播放策略。这个参数的默认值为AutoMediaPlaybackPolicy.require_user_action_for_all_media_types,表示不允许自动播放任何媒体资源。
当WebView加载包含媒体资源的页面时,这个参数允许我们控制WebView在何时启动自动播放。有三个选项可供选择:
- AutoMediaPlaybackPolicy.always_allow:允许自动播放所有媒体资源。
- AutoMediaPlaybackPolicy.always_deny:禁止自动播放所有媒体资源。
- AutoMediaPlaybackPolicy.require_user_action_only:启动媒体资源的自动播放需要用户的手动操作(例如点击媒体元素)。
initialMediaPlaybackPolicy参数使我们能够控制WebView在何时启动自动播放,从而提供更好的用户体验,并确保用户在必要时能够自己控制媒体资源的播放。
- allowFileAccess:
是否允许web端input file 打开文件选择
路由
navigationDelegate是一个重要的控制器,它用于决定WebView导航时的行为。它允许在发生页面导航时执行自定义代码逻辑,比如控制是否允许导航,跳转页面,重定向等。在应用中添加navigationDelegate能够帮助开发者更好地控制WebView的导航行为,并以此来实现更好的用户体验。
//代码来源:/jianghuAppBrowser/jianghuBrowser/lib/layout/WebViewWidget.dartwebViewWidget(context) {return ValueListenableBuilder(valueListenable: Constants.appId,builder: (context, appId, _) {print("=== appId ValueListenableBuilder: $appId");return WebView(navigationDelegate: (NavigationRequest request) async {debugPrint('navigationDelegate ${request.url}');if (request.url.toLowerCase().startsWith('blob')) {//拦截了blob请求,不做处理return NavigationDecision.prevent;}String fileName = request.url.substring(request.url.lastIndexOf('/') + 1);// 获取连接返回类型String? contentType = await Utils.getUrlResType(request.url);debugPrint('navigationDelegate.contentType $contentType');if (contentType != null && contentType.startsWith('application')) {runDownload(context, request, fileName);return NavigationDecision.prevent;}RenderKey.bottomNavBarRenderHash.value = DateTime.now().millisecondsSinceEpoch;debugPrint('navigationDelegate.navigate ${request.url}');//会继续往下走return NavigationDecision.navigate;},);},);}
事件处理
//代码来源:/jianghuAppBrowser/jianghuBrowser/lib/layout/WebViewWidget.dartwebViewWidget(context) {return ValueListenableBuilder(valueListenable: Constants.appId,builder: (context, appId, _) {print("=== appId ValueListenableBuilder: $appId");return WebView(onProgress: (int progress) async {debugPrint('onProgress $progress');RenderKey.webUrlLabel.value = await WebViewHandler().getPageTitle();RenderKey.pageLoadingProgress.value = progress;widget.onProgress(progress);},onWebViewCreated: (WebViewController webViewController) {Constants.webViewCreated = true;RenderKey.controller.value = webViewController;widget.onWebViewCreated();},onWebResourceError: (WebResourceError error) {DioUtil().webViewRequestError.value = error;debugPrint('onWebResourceError ${error.errorCode} ${error.errorCode} ${error.errorType} ${error.domain} ${error.description} ${error.failingUrl}');},onPageStarted: (String url) async {WebViewHandler().webAppInitialization();DioUtil().webViewRequestError.value = null;AuthUtil.saveCookie();},onPageFinished: (String url) async {await onPageFinished(url);},);},);}
- onWebViewCreated
当我们在应用中创建一个WebView时,我们可以指定一个回调函数来处理WebView创建的事件。在这个回调函数中,我们可以执行一些初始化操作,例如设置WebView的属性、加载本地资源等。
- onPageStarted
当我们在WebView中加载一个网页时,这个方法会被调用。在这个方法中,我们可以执行一些操作,例如显示一个进度条、更新UI等。
- onPageFinished
当我们在WebView中加载一个网页时,这个方法会在页面加载完成后被调用。在这个方法中,我们可以执行一些操作,例如隐藏进度条、更新UI等。
- onProgress
当我们在WebView中加载一个网页时,这个方法会在页面加载过程中被多次调用,每次调用都会传递一个当前加载进度的值。在这个方法中,我们可以更新进度条等UI组件,以反映当前的加载进度。
- onWebResourceError
这个回调会在WebView加载过程中发生错误时被调用。具体来说,当WebView加载一个页面或一个资源时遇到错误时,此回调函数会被调用。
onWebResourceError的作用在于允许我们在发生错误时执行特定的操作,例如显示一个错误提示,记录错误信息等。这个回调函数有两个参数,分别是错误状态码和错误描述。我们可以使用这些参数来判断错误类型并采取适当的措施。
扩展功能
拦截静态文件请求,如果是静态文件,则从/webView/webview_flutter_android-2.8.14/android/src/main/assets/public下获取对应文件,不从web获取,提升速度。
//代码来源:/jianghuAppBrowser/jianghu_packages/webView/webview_flutter_android-2.8.14/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewClientHostApiImpl.java@Overridepublic WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {long times = System.currentTimeMillis();if (request == null) {return super.shouldInterceptRequest(view, (WebResourceRequest) null);}String url = request.getUrl().toString();Log.d(this.getClass().getName(), appId + ", 请求连接::" + url);url = url.split("://")[0] + "://" + url.split("://")[1].replaceAll("\\/\\/", "/");boolean isFind = matchUrl(url);if (isFind) {initAssetsLoader(request.getUrl());try {@Nullable WebResourceResponse result = null;assert assetLoader != null;result = assetLoader.shouldInterceptRequest(Uri.parse(url));if(result == null || result.getMimeType() == null && result.getData() == null) {Log.d(this.getClass().getName(), "拦截返回本地文件:" + url + " 失败,use: " + (System.currentTimeMillis() - times) + "ms");result = super.shouldInterceptRequest(view, request);} else {Log.d(this.getClass().getName(), "拦截返回本地文件:" + url + " 成功,use: " + (System.currentTimeMillis() - times) + "ms");}return result;} catch (Exception e) {Log.d(this.getClass().getName(), "shouldInterceptRequest: localPath Exception" + e.getMessage());return super.shouldInterceptRequest(view, request);}} else {return super.shouldInterceptRequest(view, request);}}