美文网首页Flutter
flutter_inappwebview的使用与问题解决方案

flutter_inappwebview的使用与问题解决方案

作者: dushiling | 来源:发表于2022-03-06 17:17 被阅读0次
    • 前情摘要

    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

    相关文章

      网友评论

        本文标题:flutter_inappwebview的使用与问题解决方案

        本文链接:https://www.haomeiwen.com/subject/cuourrtx.html