-
前情摘要
Q1:
A1:Flutter沒(méi)有類(lèi)似WebView控件留搔,借助平臺(tái)層實(shí)現(xiàn)WebView功能迫靖。
Q2:
A2:借助現(xiàn)網(wǎng)提供的WebView插件即可實(shí)現(xiàn)網(wǎng)絡(luò)加載,其中flutter_inappwebview插件非常優(yōu)秀扇雕,推薦使用码邻。
Q3:
A3:InAppWebView已經(jīng)實(shí)現(xiàn)了一套完整的js通信機(jī)制,如果用官方WebView插件旋讹,則需要自己實(shí)現(xiàn)一套JsBridge同時(shí)適用Android和iOS殖蚕,成本稍高一點(diǎn)。
Q4:
A4:Flutter本身不提供WebView功能沉迹,通過(guò)PlatformView去適用各個(gè)平臺(tái)已有的WebView能力睦疫,降低了實(shí)現(xiàn)成本
webview_flutter:功能一般,滿(mǎn)足基本功能需求鞭呕,官方出品持續(xù)完善中蛤育。(不支持H5上傳圖片)
flutter_inappwebview:功能非常豐富,文檔非常完善,屬于三方庫(kù)中的精品瓦糕,推薦使用底洗。
flutter_webview_plugin:功能不夠完善,現(xiàn)有功能將積極合入webview_flutter咕娄,后續(xù)不在維護(hù)亥揖,不建議使用。
flutter_inappbrowser: 已停止維護(hù)
- webview_flutter 是官方維護(hù)的 WebView 插件圣勒,特性是基于原生和 Flutter SDK 封裝费变,繼承 StatefulWidget,因此支持內(nèi)嵌于 Flutter Widget 樹(shù)中圣贸,這是比較靈活的挚歧。但不支持https自制證書(shū)強(qiáng)制信任。
- flutter_webview_plugin 則是基于原生 WebView 封裝的 Flutter 插件吁峻,將原生的一些基本使用 API 封裝好提供給 Flutter 調(diào)用滑负,因此并不能內(nèi)嵌于 Flutter Widget 樹(shù)中,因此在界面的跳轉(zhuǎn)必須得先釋放掉用含,返回后又要重新初始化矮慕,所以顯示會(huì)有很多限制性。
- flutter_inappwebview 與其他WebView插件相比耕餐,它的功能 非常豐富:有很多事件 凡傅、 方法 和 選項(xiàng) 可以用來(lái)控制WebView。此外肠缔,前者沒(méi)有提供很好的API文檔,或者至少是文檔不完整哼转。相比之下明未, flutter_inappwebview 的每個(gè)特性幾乎都有文檔記錄。
-
使用flutter_inappwebview出現(xiàn)的問(wèn)題
場(chǎng)景:下面是webview中最常見(jiàn)的 需要彈出picker壹蔓,有拍照和選擇相冊(cè)功能的例子
問(wèn)題:
- 相機(jī)權(quán)限默認(rèn)是禁止的趟妥。直接跳轉(zhuǎn)到相冊(cè)。
- 開(kāi)啟相機(jī)權(quán)限佣蓉,閃退披摄。
- 授權(quán)被拒絕后,無(wú)法再?gòu)棾鍪跈?quán)
- 無(wú)法直接跳轉(zhuǎn)到相機(jī)拍照
-
問(wèn)題解決
在設(shè)置中開(kāi)啟相機(jī)權(quán)限后疚膊,再點(diǎn)擊按鈕,報(bào)如下錯(cuò)誤:
解決方法為:在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>
添加以上代碼后虾标,在相機(jī)權(quán)限開(kāi)啟的情況下寓盗,能正常彈出選擇彈框了。
但是默認(rèn)是禁止的,點(diǎn)擊按鈕無(wú)反應(yīng)傀蚌。
查看如下文件中的 startPhotoPickerIntent 方法:
正常邏輯:首次進(jìn)入基显,獲取相機(jī)授權(quán),允許訪(fǎng)問(wèn)善炫,則彈出picker選擇框撩幽,禁止訪(fǎng)問(wèn),則下次再進(jìn)入跳轉(zhuǎn)到設(shè)置開(kāi)啟箩艺。
所以修改代碼如下:
Activity activity = inAppBrowserDelegate != null ? inAppBrowserDelegate.getActivity() : plugin.activity;
if (!needsCameraPermission()) {
if (acceptsImages(acceptTypes)) {
extraIntents.add(getPhotoIntent());
}
if (acceptsVideo(acceptTypes)) {
extraIntents.add(getVideoIntent());
}
} else {
//動(dòng)態(tài)獲取權(quán)限
boolean hasrefuse = ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.CAMERA);
//選擇了禁止或拒絕
if (hasrefuse){
/**跳轉(zhuǎn)到設(shè)置中去開(kāi)啟**/
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 {
//獲取授權(quán)
ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.CAMERA}, 100);
return false;
}
}
效果如下:
-
關(guān)于shouldShowRequestPermissionRationale
shouldShowRequestPermissionRationale窜醉,回到最初的解釋“”。
1.都舅桩,用戶(hù)不一定會(huì)拒絕你酱虎,所以你不用解釋?zhuān)史祷?img class="math-inline" src="https://math.jianshu.com/math?formula=%5Ccolor%7Bbrown%7D%7Bfalse%7D" alt="\color{brown}{false}" mathimg="1">;
2.,此時(shí)返回擂涛,意思是你該向用戶(hù)好好解釋下了读串;
3.,也不給你彈窗提醒了撒妈,所以你也不用解釋了恢暖,故返回;
4.,都給你權(quán)限了狰右,還解釋個(gè)啥杰捂,故返回。
shouldShowRequestPermissionRationale的功能價(jià)值何在
在此之前先說(shuō)明下棋蚌,由于不同的系統(tǒng)廠商定制的結(jié)果嫁佳,
1.有的手機(jī)某些權(quán)限清單注冊(cè)了權(quán)限就能用,不用動(dòng)態(tài)申請(qǐng)(因?yàn)橄到y(tǒng)會(huì)在安裝時(shí)自動(dòng)app分配一些權(quán)限谷暮,具體怎么分配的這里暫不做討論)蒿往;
2.有的手機(jī)在彈出授權(quán)時(shí)選擇拒絕就默認(rèn)了不再?gòu)棾觯?br>
3.有的沿用了原生系統(tǒng)的規(guī)則;
4.設(shè)置-應(yīng)用-權(quán)限中權(quán)限分“允許湿弦、詢(xún)問(wèn)瓤漏、拒絕”三個(gè)級(jí)別,但是有的權(quán)限只有“允許颊埃、拒絕”兩個(gè)級(jí)別蔬充;
這里先統(tǒng)一下名詞:
允許 – 權(quán)限通過(guò)
拒絕–拒絕了但是還允許詢(xún)問(wèn)
禁止–拒絕了且不再允許詢(xún)問(wèn)(如4中所述的“拒絕”先定義為禁止)
不同的系統(tǒng)廠商定制的結(jié)果,
所以在測(cè)試時(shí)發(fā)現(xiàn)班利,比如RealMe手機(jī)和華為Android系統(tǒng)在權(quán)限被拒絕后
ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.CAMERA)返回的的結(jié)果為true饥漫,華為鴻蒙系統(tǒng)在被禁止后返回為false。符合上面藍(lán)色字體的解釋肥败。
詳情請(qǐng)看:shouldShowRequestPermissionRationale的詳細(xì)分析
但是上面所寫(xiě)的獲取權(quán)限的判斷邏輯趾浅,明顯在手機(jī)為華為鴻蒙系統(tǒng)(只有允許愕提、禁止)的時(shí)候,禁止后會(huì)再次走到授權(quán)方法皿哨,然而在禁止后浅侨,授權(quán)就不會(huì)再?gòu)棾隽恕?/p>
所以,應(yīng)對(duì)不同機(jī)型的情況证膨,在再次授權(quán)的事件里重寫(xiě)onRequestPermissionsResult方法如输,根據(jù)返回的requestCode結(jié)果在里面做處理。按邏輯來(lái)講央勒,應(yīng)該在如下圖的地方添加判斷做處理不见,無(wú)奈各種報(bào)錯(cuò)無(wú)法處理。如果有解決的同學(xué)請(qǐng)告知我崔步,謝謝稳吮。
所以,
在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)
//是否已經(jīng)被拒絕了
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", "被拒絕了")
}
// 權(quán)限被用戶(hù)同意井濒,可以做你要做的事情了灶似。
} else {
// 權(quán)限被用戶(hù)拒絕了,可以提示用戶(hù),關(guān)閉界面等等瑞你。
}
return
}
}
}
由于H5可能用的框架不一酪惭,所以參考下面兩篇文章添加對(duì)應(yīng)屬性:
文章1:
<input type="file" accept="image/*" capture>
文章2:
<input type="file" accept="image/*" capture="camera">
Github的Demo地址:flutter_inappwebview_demo