SingleChildScrollView 源碼定義如下:

萬(wàn)秀網(wǎng)站建設(shè)公司成都創(chuàng)新互聯(lián)公司,萬(wàn)秀網(wǎng)站設(shè)計(jì)制作,有大型網(wǎng)站制作公司豐富經(jīng)驗(yàn)。已為萬(wàn)秀1000多家提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\外貿(mào)網(wǎng)站建設(shè)要多少錢(qián),請(qǐng)找那個(gè)售后服務(wù)好的萬(wàn)秀做網(wǎng)站的公司定做!
需要注意的是, 通常 SingleChildScrollView 只應(yīng)在期望的內(nèi)容不會(huì)超過(guò)屏幕太多時(shí)使用 ,這是因?yàn)?SingleChildScrollView 不支持基于 Sliver 的延遲加載模型,所以如果預(yù)計(jì)視口可能包含超出屏幕尺寸太多的內(nèi)容時(shí),那么使用 SingleChildScrollView 將會(huì)非常昂貴(性能差),此時(shí)應(yīng)該使用一些支持Sliver延遲加載的可滾動(dòng)組件,如 ListView 。
示例1
下面是一個(gè)將大寫(xiě)字母 A-Z 沿垂直方向顯示的例子,由于垂直方向空間會(huì)超過(guò)屏幕視口高度,所以我們使用SingleChildScrollView:
示例2
示例3 - 橫向滾動(dòng)
Flutter Dio源碼分析(一)--Dio介紹
Flutter Dio源碼分析(二)--HttpClient、Http、Dio對(duì)比
Flutter Dio源碼分析(三)--深度剖析
Flutter Dio源碼分析(四)--封裝
Flutter Dio源碼分析(一)--Dio介紹視頻教程
Flutter Dio源碼分析(二)--HttpClient、Http、Dio對(duì)比視頻教程
Flutter Dio源碼分析(三)--深度剖析視頻教程
Flutter Dio源碼分析(四)--封裝視頻教程
github倉(cāng)庫(kù)地址
本文會(huì)手把手教你該怎么去封裝一個(gè)類(lèi)庫(kù),平時(shí)在我們的工作中都是拿著別人的造好的輪子在使用,這篇文章將帶你怎么去自己造輪子,以后再碰到別的類(lèi)庫(kù)需要對(duì)其進(jìn)行封裝的時(shí)候提供一個(gè)的思路和方法。
在前面的文章中,我們對(duì) Dio 的基本使用、請(qǐng)求庫(kù)對(duì)比、源碼分析,我們知道 Dio 的使用非常的簡(jiǎn)單,那為什么還需要進(jìn)行封裝呢?有兩點(diǎn)如下:
當(dāng)組件庫(kù)方法發(fā)生重要改變需要遷移的時(shí)候如果有多處地方用到,那么需要對(duì)使用到的每個(gè)文件都進(jìn)行修改,非常的繁瑣而且很容易出問(wèn)題。
當(dāng)不需要 Dio 庫(kù)的時(shí)候,我們可以隨時(shí)方便切換到別的網(wǎng)絡(luò)請(qǐng)求庫(kù),當(dāng)然 Dio 目前內(nèi)置支持使用第三方庫(kù)的適配器。
因?yàn)橐粋€(gè)應(yīng)用程序基本都是統(tǒng)一的配置方式,所以我們可以針對(duì) 攔截器 、 轉(zhuǎn)換器 、 緩存 、 統(tǒng)一處理錯(cuò)誤 、 代理配置 、 證書(shū)校驗(yàn) 等多個(gè)配置進(jìn)行統(tǒng)一管理。
因?yàn)槲覀兊膽?yīng)用程序在每個(gè)頁(yè)面中都會(huì)用到網(wǎng)絡(luò)請(qǐng)求,那么如果我們每次請(qǐng)求的時(shí)候都去實(shí)例化一個(gè) Dio ,無(wú)非是增加了系統(tǒng)不必要的開(kāi)銷(xiāo),而使用單例模式對(duì)象一旦創(chuàng)建每次訪問(wèn)都是同一個(gè)對(duì)象,不需要再次實(shí)例化該類(lèi)的對(duì)象。
這是通過(guò)靜態(tài)變量的私有構(gòu)造器來(lái)創(chuàng)建的單例模式
我們對(duì) 超時(shí)時(shí)間 、 響應(yīng)時(shí)間 、 BaseUrl 進(jìn)行統(tǒng)一設(shè)置
因?yàn)椴还苁?get() 還是 post() 請(qǐng)求, Dio 內(nèi)部最終都會(huì)調(diào)用 request 方法,只是傳入的 method 不一樣,所以我們這里定義一個(gè)枚舉類(lèi)型在一個(gè)方法中進(jìn)行處理
我們已經(jīng)把 Restful API 風(fēng)格簡(jiǎn)化成了一個(gè)方法,通過(guò) DioMethod 來(lái)標(biāo)明不同的請(qǐng)求方式。在我們平時(shí)開(kāi)發(fā)的過(guò)程中,需要在請(qǐng)求前、響應(yīng)前、錯(cuò)誤時(shí)對(duì)某一些接口做特殊的處理,那我們就需要用到攔截器。 Dio 為我們提供了自定義攔截器功能,很容易輕松的實(shí)現(xiàn)對(duì)請(qǐng)求、響應(yīng)、錯(cuò)誤時(shí)進(jìn)行攔截
我們發(fā)現(xiàn)雖然 Dio 框架已經(jīng)封裝了一個(gè) DioError 類(lèi)庫(kù),但如果需要對(duì)返回的錯(cuò)誤進(jìn)行統(tǒng)一彈窗處理或者路由跳轉(zhuǎn)等就只能自定義了
在我們發(fā)送請(qǐng)求的時(shí)候會(huì)碰到幾種情況,比如需要對(duì)非open開(kāi)頭的接口自動(dòng)加上一些特定的參數(shù),獲取需要在請(qǐng)求頭增加統(tǒng)一的 token
在我們請(qǐng)求接口前可以對(duì)響應(yīng)數(shù)據(jù)進(jìn)行一些基礎(chǔ)的處理,比如對(duì)響應(yīng)的結(jié)果進(jìn)行自定義封裝,還可以針對(duì)單獨(dú)的 url 做特殊處理等。
我們看了轉(zhuǎn)換器的介紹,發(fā)現(xiàn)和攔截器的功能差不多,那為什么還要存在轉(zhuǎn)換器,有兩點(diǎn):
執(zhí)行流程: 請(qǐng)求攔截器 請(qǐng)求轉(zhuǎn)換器 發(fā)起請(qǐng)求 響應(yīng)轉(zhuǎn)換器 響應(yīng)攔截器 最終結(jié)果 。
只會(huì)被用于 'PUT'、 'POST'、 'PATCH'方法,因?yàn)橹挥羞@些方法才可以攜帶請(qǐng)求體(request body)
會(huì)被用于所有請(qǐng)求方法的返回?cái)?shù)據(jù)。
在開(kāi)發(fā)過(guò)程中,客戶端和服務(wù)器打交道的時(shí)候,往往會(huì)用一個(gè) token 來(lái)做校驗(yàn),因?yàn)槊總€(gè)公司處理刷新token的邏輯都不一樣,我這里舉一個(gè)簡(jiǎn)單的例子
為什么我們需要有取消請(qǐng)求的功能,如果當(dāng)我們的頁(yè)面在發(fā)送請(qǐng)求時(shí),用戶主動(dòng)退出當(dāng)前界面或者app應(yīng)用程序退出的時(shí)候數(shù)據(jù)還沒(méi)有響應(yīng),那我們就需要取消該網(wǎng)絡(luò)請(qǐng)求,防止不必要的錯(cuò)誤。
由 服務(wù)器生成 的 一小段文本信息 ,發(fā)送給瀏覽器,瀏覽器把 cookie 以kv形式保存到本地 某個(gè)目錄下的文本文件內(nèi),下一次請(qǐng)求同一網(wǎng)站時(shí)會(huì)把該 cookie 發(fā)送給服務(wù)器。
cookie 的使用需要用到兩個(gè)第三方組件 dio_cookie_manager 和 cookie_jar
因?yàn)樵谖覀兤綍r(shí)的開(kāi)發(fā)過(guò)程中,會(huì)碰到一種情況,在進(jìn)行網(wǎng)絡(luò)請(qǐng)求時(shí),我們希望能正常訪問(wèn)到上次的數(shù)據(jù),對(duì)于用戶的體驗(yàn)比較好,而不是展示一個(gè)空白的頁(yè)面,該緩存主要是 《Flutter實(shí)戰(zhàn)》網(wǎng)絡(luò)接口緩存 提供參考。
我們?cè)诔绦蛲顺龊髢?nèi)存緩存將會(huì)消失,所以我們用 shared_preferences 進(jìn)行磁盤(pán)緩存數(shù)據(jù)。
在我們用flutter進(jìn)行抓包的時(shí)候需要配置 Dio 代理。由 DefaultHttpClientAdapter 提供了一個(gè) onHttpClientCreate 回調(diào)來(lái)設(shè)置底層 HttpClient 的代理。
用于驗(yàn)證正在訪問(wèn)的網(wǎng)站是否真實(shí)。提供安全性,因?yàn)樽C書(shū)和域名綁定,并且由根證書(shū)機(jī)構(gòu)簽名確認(rèn)。
日志打印主要是幫助我們開(kāi)發(fā)時(shí)進(jìn)行輔助排錯(cuò)
點(diǎn)擊 “協(xié)議、稅務(wù)和銀行業(yè)務(wù)”
內(nèi)購(gòu)用的是付費(fèi)應(yīng)用程序,先簽署《付費(fèi)應(yīng)用程序協(xié)議》,同意后狀態(tài)變更為“用戶信息待處理”,等待審核。
狀態(tài)更改完畢后,點(diǎn)擊“開(kāi)始設(shè)置稅務(wù)、銀行業(yè)務(wù)和聯(lián)系信息”。
(1)添加銀行賬戶,按照要求填寫(xiě)相關(guān)內(nèi)容即可。
(2)選擇報(bào)稅表,并填寫(xiě)。所有與 Apple 有商業(yè)合作者必選都是美國(guó),若有其他需求,可以多選。
繼續(xù)填寫(xiě),首先認(rèn)證公司基本信息,選擇所有人類(lèi)型,確認(rèn)無(wú)誤后認(rèn)證條款處打?qū)?/p>
Part I 部分,繼續(xù)核對(duì)公司相關(guān)信息,選填內(nèi)容可不填。
Part III 部分,簽署稅務(wù)條約,設(shè)置利益限制條款的種類(lèi),選填內(nèi)容可不填。此部分如果需要可勾選上下圖勾選框,不需要可不勾選,我們這個(gè)項(xiàng)目沒(méi)有用到part III 部分,所以沒(méi)有勾選。
Part XXX 部分,確認(rèn)之前填寫(xiě)的信息,勾選完畢后,提交
(3)填寫(xiě)聯(lián)系信息,共5個(gè)。高級(jí)管理、財(cái)務(wù)、技術(shù)、法務(wù)、營(yíng)銷(xiāo)。只需要提供5個(gè)人的基本信息即可。
只可使用一次的產(chǎn)品,使用之后即失效,必須再次購(gòu)買(mǎi)。
示例: 釣魚(yú) App 中的魚(yú)食。
只需購(gòu)買(mǎi)一次,不會(huì)過(guò)期或隨著使用而減少的產(chǎn)品。
示例: 游戲 App 的賽道。
允許用戶在固定時(shí)間段內(nèi)購(gòu)買(mǎi)動(dòng)態(tài)內(nèi)容的產(chǎn)品。除非用戶選擇取消,否則此類(lèi)訂閱會(huì)自動(dòng)續(xù)期。
示例: 每月訂閱提供流媒體服務(wù)的 App。
允許用戶購(gòu)買(mǎi)有時(shí)限性服務(wù)的產(chǎn)品。此 App 內(nèi)購(gòu)買(mǎi)項(xiàng)目的內(nèi)容可以是靜態(tài)的。此類(lèi)訂閱不會(huì)自動(dòng)續(xù)期。
示例: 為期一年的已歸檔文章目錄訂閱。
App 內(nèi)購(gòu)買(mǎi)項(xiàng)目的截屏,即所售項(xiàng)目的示意圖。例如,如果 App 內(nèi)購(gòu)買(mǎi)項(xiàng)目是一本圖書(shū),您可以提交圖書(shū)的截屏。您也可以提交購(gòu)買(mǎi)頁(yè)的截屏。該截屏僅用于 Apple 審核,不會(huì)在 App Store 中顯示。
截屏要求如下:
iOS 至少需要 640 x 920 像素
Apple tvOS 需要 1920 x 1080 像素
macOS 需要 1280 x 800 像素
App 審核圖像上傳后,可以替換,但無(wú)法移除。當(dāng)您的 App 內(nèi)購(gòu)買(mǎi)項(xiàng)目處于審核中時(shí),您無(wú)法更新截屏。
沙箱賬號(hào)是不能直接在App Store進(jìn)行登錄的,只能在點(diǎn)擊了購(gòu)買(mǎi)商品之后,在彈出的登錄框進(jìn)行登錄 。
驗(yàn)證是否已登錄沙箱測(cè)試賬號(hào):
設(shè)置--iTunes Store與App Store,頁(yè)面拉到最底部,會(huì)看到沙箱賬戶項(xiàng)會(huì)列出你已登錄的沙箱測(cè)試賬號(hào)!
操作方法一:打開(kāi)App Store應(yīng)用首頁(yè)滑到最下方--選中AppleID--注銷(xiāo)
操作方法二:設(shè)置--iTunes Store與App Store--選中AppleID--注銷(xiāo)
checks if the client can make payments(檢測(cè)App是否能支付)
getAvailablePurchases
Get all non-consumed purchases 獲取未消費(fèi)的商品
打印信息查詢(xún);
原因:
沒(méi)有先執(zhí)行g(shù)etProducts,直接執(zhí)行requestPurchase方法,要先拉取商品列表,再執(zhí)行購(gòu)買(mǎi)操作.
問(wèn)題描述;
1.漏單必須要處理,玩家花RMB購(gòu)買(mǎi)的東西卻丟失了,是絕對(duì)不能容忍的。所謂的漏單就是玩家已經(jīng)正常付費(fèi),卻沒(méi)有拿到該拿的道具。
解決:只要購(gòu)買(mǎi)成功,便將購(gòu)買(mǎi)記錄(receipt等賬單信息)保存下來(lái),然后將賬單信息傳送給我們游戲服務(wù)器,游戲服務(wù)器獲得賬單后,和蘋(píng)果服務(wù)器驗(yàn)證,賬單有效的話,回饋給游戲服務(wù)器處理,游戲服務(wù)器處理后,返回給游戲客戶端處理,處理完畢,將本地保存的購(gòu)買(mǎi)記錄刪除。
官方文檔:向蘋(píng)果校驗(yàn)支付憑證
21000 App Store無(wú)法讀取你提供的JSON數(shù)據(jù)
21002 收據(jù)數(shù)據(jù)不符合格式
21003 收據(jù)無(wú)法被驗(yàn)證
21004 你提供的共享密鑰和賬戶的共享密鑰不一致
21005 收據(jù)服務(wù)器當(dāng)前不可用
21006 收據(jù)是有效的,但訂閱服務(wù)已經(jīng)過(guò)期。當(dāng)收到這個(gè)信息時(shí),解碼后的收據(jù)信息也包含在返回內(nèi)容中
21007 收據(jù)信息是測(cè)試用(sandbox),但卻被發(fā)送到產(chǎn)品環(huán)境中驗(yàn)證 【請(qǐng)求sandbox校驗(yàn)支付憑證】
21008 收據(jù)信息是產(chǎn)品環(huán)境中使用,但卻被發(fā)送到測(cè)試環(huán)境中驗(yàn)證
消耗類(lèi)型: 例如:金幣、道具等。
非續(xù)訂訂閱: non-renewable subscription 例如:VIP
您的首個(gè) App 內(nèi)購(gòu)買(mǎi)項(xiàng)目必須以新的 App 版本提交。請(qǐng)創(chuàng)建您的 App 內(nèi)購(gòu)買(mǎi)項(xiàng)目,然后前往 App 的“App Store”頁(yè),從“App 內(nèi)購(gòu)買(mǎi)項(xiàng)目”中進(jìn)行選擇,點(diǎn)按“提交”。 了解更多
在上傳二進(jìn)制文件并提交首個(gè) App 內(nèi)購(gòu)買(mǎi)項(xiàng)目以供審核后,您可以使用下表提交其他 App 內(nèi)購(gòu)買(mǎi)項(xiàng)目。
唐巧-iOS應(yīng)用內(nèi)付費(fèi)(IAP)開(kāi)發(fā)步驟列表
未完~待續(xù)
當(dāng)使用內(nèi)購(gòu)購(gòu)買(mǎi)過(guò)商品之后沒(méi)有把這個(gè)交易關(guān)閉,所以再次去購(gòu)買(mǎi)商品后就會(huì)調(diào)用以前已經(jīng)購(gòu)買(mǎi)成功的交易去購(gòu)買(mǎi)因?yàn)橐呀?jīng)購(gòu)買(mǎi)過(guò),才會(huì)有這個(gè)提示
原因:添加內(nèi)購(gòu)項(xiàng)目時(shí),信息填寫(xiě)不完整,app審核圖像未上傳
處理方法:上傳app審核圖片( 合適的尺寸 ),點(diǎn)擊提交,狀態(tài)改為正在準(zhǔn)備審核中。
這個(gè)是內(nèi)購(gòu)選擇類(lèi)型不匹配原因?qū)е隆?/p>
購(gòu)買(mǎi)成功之后,Apple會(huì)返回以下四個(gè)數(shù)據(jù)給應(yīng)用
Reference
Review the updated Paid Applications Schedule.
游客身份解決方案:即不登錄也要能購(gòu)買(mǎi)
1)服務(wù)器端做一個(gè)蘋(píng)果審核機(jī)制,審核期間游客身份可以進(jìn)行一切行為,一旦審核通過(guò),修改服務(wù)端即可達(dá)到強(qiáng)制用戶登錄進(jìn)行內(nèi)購(gòu)買(mǎi)的目的(這個(gè)有點(diǎn)。。。)
2)游客可以進(jìn)行內(nèi)購(gòu)買(mǎi),購(gòu)買(mǎi)時(shí)以設(shè)備UUID為準(zhǔn),生成一個(gè)游客賬號(hào),將購(gòu)買(mǎi)信息保存在服務(wù)器和本地,當(dāng)用戶登錄正式賬戶后判斷此設(shè)備是否進(jìn)行過(guò)內(nèi)購(gòu),有的話提示用戶將游客身份購(gòu)買(mǎi)的權(quán)益與現(xiàn)有賬號(hào)綁定,如果綁定,游客權(quán)益則遷移到正式賬戶,如果不遷移,則游客身份和正是賬戶是兩個(gè)獨(dú)立賬戶,正式賬戶不享有游客身份的權(quán)益(我用的這個(gè))
內(nèi)購(gòu)游客模式解決方案
iOS內(nèi)購(gòu)規(guī)則
添加flutter_pdfview: ^1.2.1 組件
class PDFScreenextends StatefulWidget {
final Stringurl;
final Stringpath;
final Stringtitle;
PDFScreen({Key key,this.url, this.path, this.title}) :super(key: key);
_PDFScreenStatecreateState() =_PDFScreenState();
}
class _PDFScreenStateextends Statewith WidgetsBindingObserver {
final Completer_controller =
Completer();
intpages =0;
intcurrentPage =0;
boolisReady =false;
StringerrorMessage ='';
@override
Widgetbuild(BuildContext context) {
return Scaffold(
appBar:AppBar(
elevation:0,
? ? leading:new IconButton(
icon:Image.asset(
Utils.getImgPath('icon_back'),
? ? ? ? width:18,
? ? ? ? height:36,
? ? ? ),
? ? ? onPressed: () {
Navigator.of(context).pop();
? ? ? },
? ? ),
? ? centerTitle:true,
? ? title:Text(
widget.title,
? ? ? style:TextStyle(fontSize:17.0),
? ? ),
? ),
? body:Stack(
children: [
Positioned(
height: MediaQuery.of(context).size.height - (Utils.getHeightSize(80, context) *2),
? ? ? ? ? width: MediaQuery.of(context).size.width,
? ? ? ? ? child:PDFView(
filePath:widget.path,
? ? ? ? ? ? enableSwipe:true,
? ? ? ? ? ? swipeHorizontal:true,
? ? ? ? ? ? autoSpacing:false,
? ? ? ? ? ? pageFling:true,
? ? ? ? ? ? pageSnap:true,
? ? ? ? ? ? defaultPage:currentPage,
? ? ? ? ? ? fitPolicy: FitPolicy.BOTH,
? ? ? ? ? ? preventLinkNavigation:
false, // if set to true the link is handled in flutter
? ? ? ? ? ? onRender: (_pages) {
setState(() {
pages = _pages;
? ? ? ? ? ? ? ? isReady =true;
? ? ? ? ? ? ? });
? ? ? ? ? ? },
? ? ? ? ? ? onError: (error) {
setState(() {
errorMessage = error.toString();
? ? ? ? ? ? ? });
? ? ? ? ? ? ? print(error.toString());
? ? ? ? ? ? },
? ? ? ? ? ? onPageError: (page, error) {
setState(() {
errorMessage ='$page: ${error.toString()}';
? ? ? ? ? ? ? });
? ? ? ? ? ? ? print('$page: ${error.toString()}');
? ? ? ? ? ? },
? ? ? ? ? ? onViewCreated: (PDFViewController pdfViewController) {
_controller.complete(pdfViewController);
? ? ? ? ? ? },
? ? ? ? ? ? onLinkHandler: (String uri) {
print('goto uri: $uri');
? ? ? ? ? ? },
? ? ? ? ? ? onPageChanged: (int page, int total) {
print('page change: $page/$total');
? ? ? ? ? ? ? setState(() {
currentPage = page;
? ? ? ? ? ? ? });
? ? ? ? ? ? },
? ? ? ? ? ),
? ? ? ),
? ? ? Positioned(
bottom:0,
? ? ? ? ? height: Utils.getHeightSize(80, context),
? ? ? ? ? width: MediaQuery.of(context).size.width,
? ? ? ? ? child:Container(
// padding: EdgeInsets.only(left: 10.0, right: 10.0,top: 10.0,bottom: 10.0),
? ? ? ? ? ? decoration:BoxDecoration(
color: Colors.white,
? ? ? ? ? ? ? border:Border.all(color: AppColors.shadeGary),
? ? ? ? ? ? ? boxShadow: [
//refer to :
? ? ? ? ? ? ? ? BoxShadow(
color: AppColors.shadeGary,
? ? ? ? ? ? ? ? ? ? offset:Offset(0.0, 0.0),
? ? ? ? ? ? ? ? ? ? blurRadius:3.0,
? ? ? ? ? ? ? ? ? ? spreadRadius:0.0),
? ? ? ? ? ? ? ],
? ? ? ? ? ? ),
? ? ? ? ? ? child:Stack(
children: [
Row(
mainAxisSize: MainAxisSize.min,
? ? ? ? ? ? ? ? ? children: [
Container(),
? ? ? ? ? ? ? ? ? ? Expanded(child:SizedBox()),
? ? ? ? ? ? ? ? ? ? Container(
height:42.0,
? ? ? ? ? ? ? ? ? ? ? width: Utils.getWidthSize(90, context),
? ? ? ? ? ? ? ? ? ? ? margin:EdgeInsets.only(right:20.0,bottom:5.0),
? ? ? ? ? ? ? ? ? ? ? decoration:BoxDecoration(//邊框線
? ? ? ? ? ? ? ? ? ? ? ? borderRadius:BorderRadius.circular(21.0),? //圓角
? ? ? ? ? ? ? ? ? ? ? ? gradient:LinearGradient(
colors: [Color(0xFF5FD27A), Color(0xFF3FAF6F)],
? ? ? ? ? ? ? ? ? ? ? ? ),
? ? ? ? ? ? ? ? ? ? ? ),
? ? ? ? ? ? ? ? ? ? ? child:TextButton(
style:ButtonStyle(
overlayColor: MaterialStateProperty.all(Colors.transparent),
? ? ? ? ? ? ? ? ? ? ? ? ? foregroundColor: MaterialStateProperty.resolveWith(
(states) {
if (states.contains(MaterialState.pressed)) {
//按下時(shí)的顏色
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return Colors.transparent;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }
//默認(rèn)狀態(tài)使用灰色
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return Colors.transparent;
? ? ? ? ? ? ? ? ? ? ? ? ? ? },
? ? ? ? ? ? ? ? ? ? ? ? ? ),
? ? ? ? ? ? ? ? ? ? ? ? ),
? ? ? ? ? ? ? ? ? ? ? ? child:Text(
globalTranslations.text("msg_download"),
? ? ? ? ? ? ? ? ? ? ? ? ? style:TextStyle(color: Colors.white),
? ? ? ? ? ? ? ? ? ? ? ? ),
? ? ? ? ? ? ? ? ? ? ? ? onPressed: () {
launchPdfURL(widget.url);
? ? ? ? ? ? ? ? ? ? ? ? },
? ? ? ? ? ? ? ? ? ? ? ),
? ? ? ? ? ? ? ? ? ? ),
? ? ? ? ? ? ? ? ? ],
? ? ? ? ? ? ? ? ),
? ? ? ? ? ? ? ],
? ? ? ? ? ? ),
? ? ? ? ? )),
? ? ? errorMessage.isEmpty
? ? ? ? ? ? !isReady
? ? ? ? ? ?Center(
child:CircularProgressIndicator(),
? ? ? )
:Container()
:Center(
child:Text(errorMessage),
? ? ? )
],
? ),
? // floatingActionButton: FutureBuilder(
//? future: _controller.future,
//? builder: (context, AsyncSnapshot snapshot) {
//? ? if (snapshot.hasData) {
//? ? ? return FloatingActionButton.extended(
//? ? ? ? label: Text("Go to ${pages ~/ 2}"),
//? ? ? ? onPressed: () async {
//? ? ? ? ? await snapshot.data.setPage(pages ~/ 2);
//? ? ? ? },
//? ? ? );
//? ? }
//
//? ? return Container();
//? },
// ),
);
}
launchPdfURL(String url) {
launch(url);
}
}
最近一個(gè)項(xiàng)目要實(shí)現(xiàn)可以無(wú)限循環(huán)的PageView,主要思路是在初始化pageview的list的時(shí)候在開(kāi)始和結(jié)尾多加一個(gè)結(jié)尾和開(kāi)頭的widget,當(dāng)滑動(dòng)到開(kāi)頭和結(jié)尾的時(shí)候手動(dòng)進(jìn)行頁(yè)面的切換,詳細(xì)可以搜索pageview無(wú)限輪播。
這種方法有一個(gè)要點(diǎn)就是要維護(hù)兩個(gè)索引,一個(gè)是內(nèi)部list的索引,一個(gè)是外部顯示的索引,由于list的容量是比顯示的數(shù)量多2的,所以如果要在外部進(jìn)行一些比如指示器或者計(jì)時(shí)器功能要進(jìn)行和頁(yè)面同步顯示或者切換頁(yè)面操作時(shí),需要將顯示的索引轉(zhuǎn)換成list的索引。
不過(guò)網(wǎng)上說(shuō)的都是一些比較簡(jiǎn)單的實(shí)現(xiàn),看到比較多的就是當(dāng)滑動(dòng)到要手動(dòng)切換的時(shí)候進(jìn)行一個(gè)時(shí)延,這樣可以避免直接切換頁(yè)面造成的卡頓和跳動(dòng)現(xiàn)象。但是存在一個(gè)問(wèn)題,如果要同時(shí)實(shí)現(xiàn)一個(gè)跟隨頁(yè)面切換的指示器,就會(huì)出現(xiàn)當(dāng)頁(yè)面切換過(guò)去之后指示器才會(huì)跟著過(guò)去,因?yàn)轫?yè)面切換的時(shí)候執(zhí)行了時(shí)延,而時(shí)延之后才會(huì)真正改變索引,此時(shí)才會(huì)setstate,之后指示器才能響應(yīng)到索引的切換,但是如果在時(shí)延之前就切換的話又會(huì)出現(xiàn)指示器先行的情況。因此這種方法其實(shí)是存在一些問(wèn)題的。
所以解決這個(gè)問(wèn)題的關(guān)鍵在于如何進(jìn)行頁(yè)面切換的判斷。這里可以有兩種思路實(shí)現(xiàn),第一種是實(shí)現(xiàn)viewpage的onpagechanged方法,在里面進(jìn)行邏輯的判斷,然后用controller來(lái)進(jìn)行頁(yè)面跳轉(zhuǎn),不過(guò)這種方法存在當(dāng)controller跳轉(zhuǎn)的時(shí)候又會(huì)回調(diào)onpagechanged,所以就會(huì)出現(xiàn)多次對(duì)索引不必要操作,而且如果有比如計(jì)時(shí)器等額外的功能的話可能不方便將頁(yè)面邏輯分開(kāi),而且依舊無(wú)法解決指示器延遲問(wèn)題,同時(shí)也很難進(jìn)行細(xì)粒度的操作。
第二種方法我們就要去看pageview的源碼了,從源碼的角度來(lái)解決問(wèn)題才是正確的方法。首先我們點(diǎn)進(jìn)去pageview的源碼
看到這里其實(shí)已經(jīng)有一些思路了,我們之前難點(diǎn)在于重寫(xiě)了onpagechanged方法導(dǎo)致問(wèn)題無(wú)法很好的解決,現(xiàn)在我們找到了onpagechanged調(diào)用的地方,只要找辦法避免掉就可以實(shí)現(xiàn)了。
當(dāng)然這里我們要說(shuō)到NotificationListener,以及flutter對(duì)應(yīng)的冒泡事件傳輸機(jī)制,這里大家可以去看看這篇 文章 。
我來(lái)總結(jié)一下,其實(shí)就是flutter對(duì)于notification這個(gè)組件,有一中事件規(guī)則叫冒泡傳遞,底層的notification如果在它的 onNotification寫(xiě)的邏輯中返回是false以及它不是根結(jié)點(diǎn),就會(huì)去向上遍歷尋找它的祖先notification組件,知道遇到root節(jié)點(diǎn)或者某一個(gè)返回true,則事件傳遞結(jié)束。
而且在onNotification中可以對(duì)多種事件進(jìn)行監(jiān)聽(tīng)和處理,所以我們可以把對(duì)viewpage頁(yè)面跳轉(zhuǎn)對(duì)索引處理的邏輯寫(xiě)在這里,而且我們可以分別處理比如滑動(dòng)開(kāi)始的start事件和結(jié)束的end事件,分別進(jìn)行細(xì)粒度的邏輯的處理,這樣就可以在外部進(jìn)行操作和別的功能實(shí)現(xiàn)了。
因此不僅無(wú)限輪播事件可以通過(guò)這種方法來(lái)解決,如果有其他的操作也可以這樣進(jìn)行處理,而且因?yàn)槲覀儧](méi)有傳入onpagechanged方法,所以不存在多次調(diào)用的問(wèn)題,pageview那里判斷onpagechanged是null方法就不會(huì)進(jìn)去了,會(huì)直接我們寫(xiě)在pageview外面的notification的邏輯。
最后的結(jié)構(gòu)大概這樣
最近項(xiàng)目中,用到了flutter中的webview加載帶有echarts控件的html文件,但是發(fā)現(xiàn)圖表一直無(wú)法顯示,通過(guò)查詢(xún)相關(guān)資料,分析apk包文件,知道了是由于flutter項(xiàng)目中的文件打包后的路徑發(fā)生變化,造成找不到相關(guān)依賴(lài),特此記錄一下,使用下面這個(gè)方法便可以解析出Android和IOS打包后的apk中資源文件的實(shí)際位置。
例如,你的資源文件是在flutter項(xiàng)目的根目錄下的 'assets/files/echart-pie.html',
那 'getAssetsPath('assets/files/echart-pie.html')'才是打包成apk后,資源文件的實(shí)際路徑。
按照上面的操作,你會(huì)發(fā)現(xiàn)在android設(shè)備上已經(jīng)可以正常顯示本地html頁(yè)面了,但是IOS設(shè)備上還是一臉懵逼;不要慌,這塊是由于webview_flutter本來(lái)就不支持加載本地文件,這時(shí)我們還需要修改下插件IOS部分的源碼,F(xiàn)lutterWebView.m文件:
通過(guò)以上騷操作,就可以實(shí)現(xiàn)webview_flutter在android和ios設(shè)備上加載本地html文件了。
最后:推薦一個(gè)插件,已經(jīng)在官方的基礎(chǔ)上進(jìn)行了優(yōu)化
webview_flutter_plus
網(wǎng)頁(yè)標(biāo)題:蘋(píng)果flutter源碼,flutter源代碼
本文路徑:http://chinadenli.net/article24/dsgjece.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供ChatGPT、微信公眾號(hào)、做網(wǎng)站、外貿(mào)建站、面包屑導(dǎo)航、云服務(wù)器
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)