Flutter開發(fā)通用頁面Loading組件示例詳解
前沿
頁面通用Loading組件是一個App必不可少的基礎(chǔ)功能,之前只開發(fā)過Android原生的頁面Loading,這次就按原生的邏輯再開發(fā)一個Flutter的Widget,對其進行封裝復(fù)用
我們先看下效果:
原理
狀態(tài)
一個通用的頁面加載Loading組件應(yīng)該具備以下幾種狀態(tài):
IDLE 初始化
Idle狀態(tài),此時的組件還只是初始化
LOADING 加載中
Loading狀態(tài),一般在網(wǎng)絡(luò)請求或者耗時加載數(shù)據(jù)時調(diào)用,通用顯示的是一個progress或者自定義的幀動畫
LOADING_SUCCESS
LoadingSuccess加載成功,一般在網(wǎng)絡(luò)請求成功后調(diào)用,并將需要展示的頁面展示出來
LOADING_SUCCESS_BUT_EMPTY
頁面加載成功但是沒有數(shù)據(jù),這種情況一般是發(fā)起列表數(shù)據(jù)請求但是沒有數(shù)據(jù),通常我們會展示一個空數(shù)據(jù)的頁面來提醒用戶
NETWORK_BLOCKED
網(wǎng)絡(luò)錯誤,一般是由于網(wǎng)絡(luò)異常、網(wǎng)絡(luò)請求連接超時導(dǎo)致。此時我們需要展示一個網(wǎng)絡(luò)錯誤的頁面,并且?guī)в兄卦嚢粹o,讓用戶重新發(fā)起請求
ERROR
通常是接口錯誤,這種情況下我們會根據(jù)接口返回的錯誤碼或者錯誤文本提示用戶,并且也有重試按鈕
/// 狀態(tài)枚舉 enum LoadingStatus { idle, // 初始化 loading, // 加載中 loading_suc, // 加載成功 loading_suc_but_empty, // 加載成功但是數(shù)據(jù)為空 network_blocked, // 網(wǎng)絡(luò)加載錯誤 error, // 加載錯誤 }
點擊事件回調(diào)
當網(wǎng)絡(luò)異常或者接口報錯時,會顯示錯誤頁面,并且提供重試按鈕,讓用戶點擊重新請求。基于這個需求,我們還需要提供點擊重試后的事件回調(diào)讓業(yè)務(wù)可以處理重新請求。
/// 定義點擊事件 typedef OnTapCallback = Function(LoadingView widget);
提示文案
提供提示文案的自定義,方便業(yè)務(wù)根據(jù)自己的需求展示特定的提示文案
代碼實現(xiàn)
根據(jù)上面的原理來實現(xiàn)對應(yīng)的代碼
- 構(gòu)造方法
/// 構(gòu)造方法 LoadingView({ Key key, @required this.child, // 需要加載的Widget @required this.todoAfterError, // 錯誤點擊重試 @required this.todoAfterNetworkBlocked, // 網(wǎng)絡(luò)錯誤點擊重試 this.networkBlockedDesc = "網(wǎng)絡(luò)連接超時,請檢查你的網(wǎng)絡(luò)環(huán)境", this.errorDesc = "加載失敗", this.loadingStatus = LoadingStatus.idle, }) : super(key: key);
- 根據(jù)不同的Loading狀態(tài)展示對應(yīng)的Widget
- 其中idle、success狀態(tài)直接展示需要加載的Widget(這里也可以使用漸變動畫進行切換過度)
///根據(jù)不同狀態(tài)展示不同Widget Widget _buildBody() { switch (widget.loadingStatus) { case LoadingStatus.idle: return widget.child; case LoadingStatus.loading: return _buildLoadingView(); case LoadingStatus.loading_suc: return widget.child; case LoadingStatus.loading_suc_but_empty: return _buildLoadingSucButEmptyView(); case LoadingStatus.error: return _buildErrorView(); case LoadingStatus.network_blocked: return _buildNetworkBlockedView(); } return widget.child; }
- buildLoadingView,這里簡單用了系統(tǒng)的CircularProgressIndicator,也可以自己顯示幀動畫
/// 加載中 View Widget _buildLoadingView() { return Container( width: double.maxFinite, height: double.maxFinite, child: Center( child: SizedBox( height: 22.w, width: 22.w, child: CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation<Color>(AppColors.primaryBgBlue), ), ), ), ); }
- 其他提示頁面,這里做了一個統(tǒng)一的封裝
/// 編譯通用頁面 Container _buildGeneralTapView({ String url = "images/icon_network_blocked.png", String desc, @required Function onTap, }) { return Container( color: AppColors.primaryBgWhite, width: double.maxFinite, height: double.maxFinite, child: Center( child: SizedBox( height: 250.h, child: Column( children: [ Image.asset(url, width: 140.w, height: 99.h), SizedBox( height: 40.h, ), Text( desc, style: AppText.gray50Text12, maxLines: 2, overflow: TextOverflow.ellipsis, ), SizedBox( height: 30.h, ), if (onTap != null) BorderRedBtnWidget( content: "重新加載", onClick: onTap, padding: 40.w, ), ], ), ), ), ); } /// 加載成功但數(shù)據(jù)為空 View Widget _buildLoadingSucButEmptyView() { return _buildGeneralTapView( url: "images/icon_empty.png", desc: "暫無數(shù)據(jù)", onTap: null, ); } /// 網(wǎng)絡(luò)加載錯誤頁面 Widget _buildNetworkBlockedView() { return _buildGeneralTapView( url: "images/icon_network_blocked.png", desc: widget.networkBlockedDesc, onTap: () { widget.todoAfterNetworkBlocked(widget); }); } /// 加載錯誤頁面 Widget _buildErrorView() { return _buildGeneralTapView( url: "images/icon_error.png", desc: widget.errorDesc, onTap: () { widget.todoAfterError(widget); }); }
使用
Widget _buildBody() { var loadingView = LoadingView( loadingStatus: LoadingStatus.loading, child: _buildContent(), todoAfterNetworkBlocked: (LoadingView widget) { // 網(wǎng)絡(luò)錯誤,點擊重試 widget.updateStatus(LoadingStatus.loading); Future.delayed(Duration(milliseconds: 1000), () { widget.updateStatus(LoadingStatus.error); }); }, todoAfterError: (LoadingView widget) { // 接口錯誤,點擊重試 widget.updateStatus(LoadingStatus.loading); Future.delayed(Duration(milliseconds: 1000), () { // widget.updateStatus(LoadingStatus.loading_suc); widget.updateStatus(LoadingStatus.loading_suc_but_empty); }); }, ); Future.delayed(Duration(milliseconds: 1000), (){ loadingView.updateStatus(LoadingStatus.network_blocked); }); return loadingView; }
總結(jié)
至此已經(jīng)完成了對整個Loading組件的封裝,代碼已上傳Github
以上就是Flutter開發(fā)通用頁面Loading組件示例詳解的詳細內(nèi)容,更多關(guān)于Flutter通用頁面Loading組件的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
android實現(xiàn)點擊按鈕切換不同的fragment布局
這篇文章主要為大家詳細介紹了android實現(xiàn)點擊按鈕切換不同的fragment布局,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-12-12Android動態(tài)修改ToolBar的Menu菜單示例
本篇文章主要介紹了Android動態(tài)修改ToolBar的Menu菜單示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-02-02