這類的庫在pub上有不少git
我爲何要自定義呢github
首先是項目須要,而且這種庫普適性高,抽取出來從此複用也方便點bash
另外記錄一下編碼思路,方便後續查看markdown
onLoadMore是加載更多時的回調,由外部實現this
isFinish 加載完成編碼
delegate是一個抽象類
spa
LoadMoreTextBuilder 是一個根據狀態構建文字的方案,默認實現了 中文/英文文字,若是隻想修改文字,使用默認樣式的話,能夠直接用這個便可3d
首先考慮怎麼自定義
通常來說有2種方式,一個是到底部繼續上拉加載,另外一種是滾動到底部自動加載,我這裏採起的是到底部自動加載方案
不使用上拉加載的緣由是:滾動到底繼續上拉不符合正常人習慣,若是是慣性滾動到底,誰知道你後面還有沒有東西的
首先怎麼樣能夠知道滾動到底了呢,最簡單的方式,listview的最後一行build的時候必定滾動到底了
因此咱們可使用以下的方式定義
class _ListViewDemoPageState extends State<ListViewDemoPage> {
var count = 10;
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: count + 1,
itemBuilder: _buildItem,
);
}
Widget _buildItem(BuildContext context, int index) {
if (index == count) {
return Text('到底了');
}
return Text(index.toString());
}
}
複製代碼
這樣的話 只要到最後了,你天然知道應該加載了
但是這個方法不優雅啊,咱們應該封裝爲Widget控件,方便複用,接下來就要開始分析怎麼自定義了
首先觀察ListView的構造方法
有以下幾個構造方法
ListView
:同名構造方法
ListView.builder
ListView.separated
ListView.custom
這裏custom,須要自定義childrenDelegate 類型爲SliverChildDelegate,我先暫時不考慮
看其餘的三個,發現內部都實現了這個Delegate
那麼咱們接收一個ListView,而後判斷其中的delegate類型,而後分別進行處理不就能夠了嗎
這裏咱們分別處理,一個是增長一個count,另外一個由於直接獲取到了List ,把loadmore的widget添加進去就好了
這裏就完成了第一部,在調用方不發生變化的狀況下,咱們獲取ListView,而且在底部添加了一個loadmore 的 widget
構造widget
這裏要思考了,咱們一共須要幾種狀態
一個是默認時的狀態,這個基本很難見到,也就是空閒狀態
一個是加載中
一個是加載失敗
一個是沒更多的數據
到底這種狀態,由於控件理論上不該該控制數據,因此必須由外部傳入
其餘的狀態包含在內部
enum LoadMoreStatus {
/// 空閒中,表示當前等待加載
///
/// wait for loading
idle,
/// 刷新中,不該該繼續加載,等待future返回
///
/// the view is loading
loading,
/// 刷新失敗,刷新失敗,這時須要點擊才能刷新
///
/// loading fail, need tap view to loading
fail,
/// 沒有更多,沒有更多數據了,這個狀態不觸發任何條件
///
/// not have more data
nomore,
}
複製代碼
這裏寫一個enum用於標示狀態
而後就是構建widget了
這裏咱們根據一些方法獲得狀態,而且構建一個StatefulWidget
返回
這裏之因此這麼作,是由於若是返回是無狀態的Widget,則二次滾動到底時,不會再次自動觸發build方法
Widget _buildLoadMoreView() {
if (widget.isFinish == true) {
this.status = LoadMoreStatus.nomore;
} else {
if (this.status == LoadMoreStatus.nomore) {
this.status = LoadMoreStatus.idle;
}
}
return NotificationListener<_RetryNotify>(
child: NotificationListener<_BuildNotify>(
child: DefaultLoadMoreView(
status: status,
delegate: loadMoreDelegate,
textBuilder: widget.textBuilder,
),
onNotification: _onLoadMoreBuild,
),
onNotification: _onRetry,
);
}
複製代碼
這裏之因此有兩個NotifycationListener是用於捕捉內部返回的兩種動做,一個是自動刷新的動做
一個是點擊重試的動做
觸發動做後,我這裏就會修改this.status setState,這樣就會觸發loadmorev View的狀態變化
這裏應用到了一個Flutter的機制,Notification機制,Widget內部通知,外部父節點NotificationListener進行捕獲,而後根據捕獲時的返回值決定是否繼續傳遞.
DefaultLoadMoreView
是一個StatefulWidget,在內部定義了點擊事件和加載事件
由於一旦顯示就自動構建,因此內部會根據狀態,只有當idle狀態時會傳遞出一個加載的通知,而後上層的Widget獲取Notify後,修改狀態爲loading,並調取加載數據的藉口
同理,當加載狀態爲錯誤時,內部就不會拋出notify了,除非在LoadMore的調用方修改finish狀態,不然理論上widget就不會再變化,不論滾動與否,這時須要用戶主動點擊加載重試,這裏在點擊事件中拋出重試的通知,外部加載
這裏之因此有一個延時,目的是爲了防止setState太頻繁形成界面不變化的問題,理論上這裏大於16ms就能夠了
而後咱們回到捕獲處
整體代碼只有300行,能夠在pub裏直接使用,目前最新版本爲0.1.1
pub地址 pub國內鏡像
github
歡迎issue 歡迎star