flutter_inappwebview的使用与问题解决方案

  • 前情摘要

Q1:\color{OrangeRed}{flutter中是否有类似原生的WebView控件}
A1:Flutter没有类似WebView控件,借助平台层实现WebView功能。
Q2:\color{OrangeRed}{flutter中如何使用WebView加载网页}
A2:借助现网提供的WebView插件即可实现网络加载,其中flutter_inappwebview插件非常优秀,推荐使用。
Q3:\color{OrangeRed}{flutter的WebView如何与js通信的}
A3:InAppWebView已经实现了一套完整的js通信机制,如果用官方WebView插件,则需要自己实现一套JsBridge同时适用Android和iOS,成本稍高一点。
Q4:\color{OrangeRed}{flutter中WebView是如何实现}
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. 无法直接跳转到相机拍照
  • 问题解决

\color{brown}{<问题1、2>}
在设置中开启相机权限后,再点击按钮,报如下错误:

error.png

解决方法为:在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>

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


1.gif

\color{brown}{<问题3>}
但是默认是禁止的,点击按钮无反应。
查看如下文件中的 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,回到最初的解释“\color{OrangeRed}{应不应该解释下}\color{green}{请求这个权限的目的}”。
1.都\color{blue}{没有请求过这个权限},用户不一定会拒绝你,所以你不用解释,故返回\color{brown}{false};
2.\color{blue}{请求了但是被拒绝了},此时返回\color{brown}{true},意思是你该向用户好好解释下了;
3.\color{blue}{请求权限被禁止了},也不给你弹窗提醒了,所以你也不用解释了,故返回\color{brown}{false};
4.\color{blue}{请求被允许了},都给你权限了,还解释个啥,故返回\color{brown}{false}

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<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
            }
        }
    }

\color{brown}{<问题4>}
由于H5可能用的框架不一,所以参考下面两篇文章添加对应属性:
文章1

<input type="file" accept="image/*" capture>

文章2

<input type="file" accept="image/*" capture="camera">

Github的Demo地址:flutter_inappwebview_demo

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,108评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,699评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,812评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,236评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,583评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,739评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,957评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,704评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,447评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,643评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,133评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,486评论 3 256
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,151评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,889评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,782评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,681评论 2 272

推荐阅读更多精彩内容