-
前情摘要
Q1:
A1:Flutter没有类似WebView控件,借助平台层实现WebView功能。
Q2:
A2:借助现网提供的WebView插件即可实现网络加载,其中flutter_inappwebview插件非常优秀,推荐使用。
Q3:
A3:InAppWebView已经实现了一套完整的js通信机制,如果用官方WebView插件,则需要自己实现一套JsBridge同时适用Android和iOS,成本稍高一点。
Q4:
A4:Flutter本身不提供WebView功能,通过PlatformView去适用各个平台已有的WebView能力,降低了实现成本
webview_flutter:功能一般,满足基本功能需求,官方出品持续完善中。(不支持H5上传图片)
flutter_inappwebview:功能非常丰富,文档非常完善,属于三方库中的精品,推荐使用。
flutter_webview_plugin:功能不够完善,现有功能将积极合入webview_flutter,后续不在维护,不建议使用。
flutter_inappbrowser: 已停止维护
- webview_flutter 是官方维护的 WebView 插件,特性是基于原生和 Flutter SDK 封装,继承 StatefulWidget,因此支持内嵌于 Flutter Widget 树中,这是比较灵活的。但不支持https自制证书强制信任。
- flutter_webview_plugin 则是基于原生 WebView 封装的 Flutter 插件,将原生的一些基本使用 API 封装好提供给 Flutter 调用,因此并不能内嵌于 Flutter Widget 树中,因此在界面的跳转必须得先释放掉,返回后又要重新初始化,所以显示会有很多限制性。
- flutter_inappwebview 与其他WebView插件相比,它的功能 非常丰富:有很多事件 、 方法 和 选项 可以用来控制WebView。此外,前者没有提供很好的API文档,或者至少是文档不完整。相比之下, flutter_inappwebview 的每个特性几乎都有文档记录。
-
使用flutter_inappwebview出现的问题
场景:下面是webview中最常见的 需要弹出picker,有拍照和选择相册功能的例子
问题:
- 相机权限默认是禁止的。直接跳转到相册。
- 开启相机权限,闪退。
- 授权被拒绝后,无法再弹出授权
- 无法直接跳转到相机拍照
-
问题解决
在设置中开启相机权限后,再点击按钮,报如下错误:
解决方法为:在project->app->android->app->src->mian里的 AndroidManifest.xml 的 application 中添加下面代码
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.flutter_inappwebview.fileprovider"
android:exported="false"
android:grantUriPermissions="true"
>
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
添加以上代码后,在相机权限开启的情况下,能正常弹出选择弹框了。
但是默认是禁止的,点击按钮无反应。
查看如下文件中的 startPhotoPickerIntent 方法:
正常逻辑:首次进入,获取相机授权,允许访问,则弹出picker选择框,禁止访问,则下次再进入跳转到设置开启。
所以修改代码如下:
Activity activity = inAppBrowserDelegate != null ? inAppBrowserDelegate.getActivity() : plugin.activity;
if (!needsCameraPermission()) {
if (acceptsImages(acceptTypes)) {
extraIntents.add(getPhotoIntent());
}
if (acceptsVideo(acceptTypes)) {
extraIntents.add(getVideoIntent());
}
} else {
//动态获取权限
boolean hasrefuse = ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.CAMERA);
//选择了禁止或拒绝
if (hasrefuse){
/**跳转到设置中去开启**/
Intent settingsIntent = new Intent();
settingsIntent.setAction(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
settingsIntent.addCategory(Intent.CATEGORY_DEFAULT);
settingsIntent.setData(android.net.Uri.parse("package:" + activity.getPackageName()));
settingsIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
settingsIntent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
settingsIntent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
activity.startActivity(settingsIntent);
return false;
}else {
//获取授权
ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.CAMERA}, 100);
return false;
}
}
效果如下:
-
关于shouldShowRequestPermissionRationale
shouldShowRequestPermissionRationale,回到最初的解释“”。
1.都,用户不一定会拒绝你,所以你不用解释,故返回;
2.,此时返回,意思是你该向用户好好解释下了;
3.,也不给你弹窗提醒了,所以你也不用解释了,故返回;
4.,都给你权限了,还解释个啥,故返回。
shouldShowRequestPermissionRationale的功能价值何在
在此之前先说明下,由于不同的系统厂商定制的结果,
1.有的手机某些权限清单注册了权限就能用,不用动态申请(因为系统会在安装时自动app分配一些权限,具体怎么分配的这里暂不做讨论);
2.有的手机在弹出授权时选择拒绝就默认了不再弹出;
3.有的沿用了原生系统的规则;
4.设置-应用-权限中权限分“允许、询问、拒绝”三个级别,但是有的权限只有“允许、拒绝”两个级别;
这里先统一下名词:
允许 – 权限通过
拒绝–拒绝了但是还允许询问
禁止–拒绝了且不再允许询问(如4中所述的“拒绝”先定义为禁止)
不同的系统厂商定制的结果,
所以在测试时发现,比如RealMe手机和华为Android系统在权限被拒绝后
ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.CAMERA)返回的的结果为true,华为鸿蒙系统在被禁止后返回为false。符合上面蓝色字体的解释。
详情请看:shouldShowRequestPermissionRationale的详细分析
但是上面所写的获取权限的判断逻辑,明显在手机为华为鸿蒙系统(只有允许、禁止)的时候,禁止后会再次走到授权方法,然而在禁止后,授权就不会再弹出了。
所以,应对不同机型的情况,在再次授权的事件里重写onRequestPermissionsResult方法,根据返回的requestCode结果在里面做处理。按逻辑来讲,应该在如下图的地方添加判断做处理,无奈各种报错无法处理。如果有解决的同学请告知我,谢谢。
所以,
在project->app->android->app->src->mian->MainActivity.kt中添加如下代码:
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String?>,
grantResults: IntArray
) {
when (requestCode) {
100 -> {
if (grantResults.size > 0) {
if (grantResults[0] == PackageManager.PERMISSION_DENIED) {
val pref1 = getSharedPreferences("data", MODE_PRIVATE)
val account = pref1.getBoolean("hasDENIED", false)
//是否已经被拒绝了
if (account) {
val settingsIntent = Intent()
settingsIntent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
settingsIntent.addCategory(Intent.CATEGORY_DEFAULT)
settingsIntent.data = Uri.parse("package:" + activity.packageName)
settingsIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
settingsIntent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)
settingsIntent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
startActivity(settingsIntent)
}else{
pref1.edit().putBoolean("hasDENIED", true).commit()
}
Log.d("JumpChannel", "被拒绝了")
}
// 权限被用户同意,可以做你要做的事情了。
} else {
// 权限被用户拒绝了,可以提示用户,关闭界面等等。
}
return
}
}
}
由于H5可能用的框架不一,所以参考下面两篇文章添加对应属性:
文章1:
<input type="file" accept="image/*" capture>
文章2:
<input type="file" accept="image/*" capture="camera">
Github的Demo地址:flutter_inappwebview_demo