Flutter 基類BaseWidget封裝(錯誤態、載入態、無資料態)

Cheney2006發表於2020-03-16

  Flutter中頁面沒有像Android、IOS 定義那麼明確,Android中就是 Activity,IOS 中就是 UIViewController。
  在Flutter中,幾乎所有東西都是Widget。

將Widget視為可視元件(或與應用程式的可視方面互動的元件)。

  在專案開發中基本都會有一個 BaseActivity/BaseVIewController,封裝一些基本操作及公共部分,參照Android/IOS中 BaseActivity/BaseVIewController實現,在 Flutter也實現同樣功能的 抽象的BaseWidget。

實現功能

  1. 開發頁面時,寫一個類繼承 BaseWidget,就會提示必須要實現的抽象方法,俗稱模板模式,要做的事情一目瞭然。這裡將要寫的程式碼定義成一個 Live Templates。
  2. 導航欄AppBar可以輕鬆隱藏和設定背景顏色(圖片),其中包含導航欄中的左邊返回鍵、中間的標題、右面的按鈕或文字,可以隨意設定隱藏和現實,當然可以重寫他們的方法,隨意設定自己的Widget,實現高度定製。
  3. 內建了一個錯誤頁面 ,主要用於網路載入出錯或伺服器返回錯誤的時候 ,可以直接調方法就顯示,不用每個頁面都寫錯誤頁面,避免需求變更時束手無策。
  4. 內建一個無資料頁面,用於列表無資料時展示。使用方法和功能和錯誤頁面差不多。
  5. 內建一個loading載入頁面,主要用於進入頁面時顯示一個 loading 檢視,預設進入介面就顯示,資料載入完成,顯示正常內容檢視,載入失敗話,就顯示上面定義的錯誤檢視。
  6. 進入自動呼叫 queryData 進行網路請求。
  7. 內建了安全鍵盤,在要使用安全鍵盤頁面,初始化一下就可以了。
  8. 。。。
  9. 有了基類,以後擴充就很方面(如:返回重新整理等)。

實現思路

BaseWidget 是一個有狀態的基礎 Widget,由兩部分組成。

Widget 定義

abstract class BaseWidget extends StatefulWidget {
  @override
  BaseWidgetState createState() => getState();

  ///子類實現
  BaseWidgetState getState();
}

複製程式碼

此部分在Widget的生命週期內不會發生變化,但可以接受可由其相應的State例項使用的引數,當需要將其新增到視窗上時,可以通過例項化來實現。

Widget State 定義

abstract class BaseWidgetState<T extends BaseWidget> extends State<T> {
@override
void initState() {
  super.initState();
}


@override
Widget build(BuildContext context) {
  ...
  }

}

複製程式碼

此部分是在Widget的生命週期中變化的部分,並強制每次應用修改時重建Widget的這個特定例項。 此兩部分別被子類繼承,並要求實現其抽象方法,getState 用於建立繼承BaseWidgetState的子類 Widget State例項。

佈局搭建

在 BaseWidgetState 中內建了標題欄、錯誤檢視、空頁面檢視、載入中檢視、安全鍵盤等幾個基本控制元件,預留一個抽象方法buildWidget給子類搭建自己的佈局。

@override
Widget build(BuildContext context) {
  return _hasSecurityKeyboard
      ? _buildWidgetWithKeyboard()
      : _buildWidgetDefault();
}

///構建預設檢視
Widget _buildWidgetDefault() {
  ///使用appbar,也可直接只有 body 在 body 裡自定義狀態列、標題欄
  return WillPopScope(
    child: Scaffold(
      appBar: _buildAppBar(),
      body: _buildBody(),
    ),
    onWillPop: _requestPop,
  );
}

///構建包含安全鍵盤檢視
Widget _buildWidgetWithKeyboard() {
  ///WidgetsApp或者MaterialApp,Flutter會自動預設建立一個Navigator
  ///用於鍵盤彈出的時候頁面可以滾動到輸入框的位置
  return WillPopScope(
    child: KeyboardMediaQuery(
      child: Builder(builder: (ctx) {
        ///初始化鍵盤監聽並且傳遞當前頁面的context
        KeyboardManager.init(ctx);
        return Scaffold(
          appBar: _buildAppBar(),
          body: _buildBody(),
        );
      }),
    ),
    onWillPop: _requestPop,
  );
}

///物理返回
Future<bool> _requestPop() {
  bool b = true;
  if (KeyboardManager.isShowKeyboard) {
    KeyboardManager.hideKeyboard();
    b = false;
  }
  return Future.value(b);
}

///子類實現,構建各自頁面UI控制元件 相當於setContentView()
Widget buildWidget(BuildContext context);

///構建內容區
Widget _buildBody() {
  return Container(
    ///內容區背景顏色
    color: LcfarmColor.colorF8F9F8,
    child: Stack(
      children: <Widget>[
        Offstage(
          offstage: !_isContentWidgetShow,
          child: buildWidget(context),
        ),
        _buildErrorWidget(),
        _buildEmptyWidget(),
        _buildLoadingWidget(),
      ],
    ),
  );
}

複製程式碼
  • 通過 _hasSecurityKeyboard控制是否內建安全鍵盤,子類可以在 initState中通過初始化該欄位來實現。
  • 同樣錯誤檢視、空白檢視、載入中檢視提供方法讓子類控制是否顯示,以及設定相應的文字、圖片等。

使用方法

  • 基本實現程式碼如下:
class Test extends BaseWidget {
  @override
  BaseWidgetState getState() {
    return _TestState();
  }
}

class _TestState extends BaseWidgetState<Test> {
  @override
  Widget buildWidget(BuildContext context) {
    return Container();
  }
  
  @override
  void queryData() {
   
  }
}

複製程式碼
  • getState 返回繼承BaseWidgetState的 Widget State。
  • buildWidget 用於構建當前檢視
  • queryData用於是否進入查詢資料

如有查詢,則直接呼叫網路請求即可。

@override
void queryData() {
  _loadData(...)
}
複製程式碼

如無需查詢資料,則禁用掉即可。

@override
void queryData() {
  ///進入無需網路請求資料
  disabledLoading();
}
複製程式碼
  • 顯示成功檢視、錯誤檢視、空白檢視
void _loadData(LoadType type) {
  AccountService.letterList(
    pageNo,
    (Pager<LetterModel> pager) {
      ///暫無資料
      if (pager.total == 0) {
        showLoadEmpty();
        return;
      }
      pageNo = pager.currentPage;
      if (type == LoadType.loadMore) {
        letterModels.addAll(pager.list);
      } else {
        letterModels = pager.list;
      }

      showLoadSuccess();
    },
    (HttpError error) {
      showLoadFailure(error.code, error.message);
    },
  );
}





複製程式碼

最後

  如果在使用過程遇到問題,歡迎下方留言交流。

學習資料

請大家不吝點贊!因為您的點贊是對我最大的鼓勵,謝謝!

相關文章