Flutter中頁面沒有像Android、IOS 定義那麼明確,Android中就是 Activity,IOS 中就是 UIViewController。
在Flutter中,幾乎所有東西都是Widget。
將Widget視為可視元件(或與應用程式的可視方面互動的元件)。
在專案開發中基本都會有一個 BaseActivity/BaseVIewController,封裝一些基本操作及公共部分,參照Android/IOS中 BaseActivity/BaseVIewController實現,在 Flutter也實現同樣功能的 抽象的BaseWidget。
實現功能
- 開發頁面時,寫一個類繼承 BaseWidget,就會提示必須要實現的抽象方法,俗稱模板模式,要做的事情一目瞭然。這裡將要寫的程式碼定義成一個 Live Templates。
- 導航欄AppBar可以輕鬆隱藏和設定背景顏色(圖片),其中包含導航欄中的左邊返回鍵、中間的標題、右面的按鈕或文字,可以隨意設定隱藏和現實,當然可以重寫他們的方法,隨意設定自己的Widget,實現高度定製。
- 內建了一個錯誤頁面 ,主要用於網路載入出錯或伺服器返回錯誤的時候 ,可以直接調方法就顯示,不用每個頁面都寫錯誤頁面,避免需求變更時束手無策。
- 內建一個無資料頁面,用於列表無資料時展示。使用方法和功能和錯誤頁面差不多。
- 內建一個loading載入頁面,主要用於進入頁面時顯示一個 loading 檢視,預設進入介面就顯示,資料載入完成,顯示正常內容檢視,載入失敗話,就顯示上面定義的錯誤檢視。
- 進入自動呼叫 queryData 進行網路請求。
- 內建了安全鍵盤,在要使用安全鍵盤頁面,初始化一下就可以了。
- 。。。
- 有了基類,以後擴充就很方面(如:返回重新整理等)。
實現思路
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);
},
);
}
複製程式碼
最後
如果在使用過程遇到問題,歡迎下方留言交流。