flutter_inappwebview的使用与问题解决方案

  • 前情摘要

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 的每个特性几乎都有文档记录。
webview.jpg
对比.png
  • 使用flutter_inappwebview出现的问题

场景:下面是webview中最常见的 需要弹出picker,有拍照和选择相册功能的例子

upimage.png

image.png

问题

  1. 相机权限默认是禁止的。直接跳转到相册。
  2. 开启相机权限,闪退。
  3. 授权被拒绝后,无法再弹出授权
  4. 无法直接跳转到相机拍照
  • 问题解决


在设置中开启相机权限后,再点击按钮,报如下错误:

error.png

解决方法为:在project->app->android->app->src->mian里的 AndroidManifest.xml 的 application 中添加下面代码

 
           
       

添加以上代码后,在相机权限开启的情况下,能正常弹出选择弹框了。


1.gif


但是默认是禁止的,点击按钮无反应。
查看如下文件中的 startPhotoPickerIntent 方法:

image.png

startPhotoPickerIntent.png

正常逻辑:首次进入,获取相机授权,允许访问,则弹出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;
      }
    }

效果如下:

1.gif
2.gif
  • 关于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结果在里面做处理。按逻辑来讲,应该在如下图的地方添加判断做处理,无奈各种报错无法处理。如果有解决的同学请告知我,谢谢。

RequestPermissionHandler.png

所以,
在project->app->android->app->src->mian->MainActivity.kt中添加如下代码:

  override fun onRequestPermissionsResult(
            requestCode: Int,
            permissions: Array,
            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:


文章2:


Github的Demo地址:flutter_inappwebview_demo

你可能感兴趣的:(flutter_inappwebview的使用与问题解决方案)