flutter 如何自定義一個loadmore / 加載更多

寫在前面

這類的庫在pub上有不少git

我爲何要自定義呢github

首先是項目須要,而且這種庫普適性高,抽取出來從此複用也方便點bash

另外記錄一下編碼思路,方便後續查看markdown

pub地址 pub國內鏡像
githubide

使用說明

導入說明看這裏中文鏡像ui

image.png

看看構造方法
一共5個屬性
child是ListView

onLoadMore是加載更多時的回調,由外部實現this

isFinish 加載完成編碼

delegate是一個抽象類
spa

image.png

有默認實現, 其中有3個方法,一個是根據狀態給一個widget高度
一個是延遲加載的毫秒時間
一個是構建顯示在內部的Widget,這樣就徹底實現了外部可根據狀態自定義Widget

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

image.png

image.png

image.png

那麼咱們接收一個ListView,而後判斷其中的delegate類型,而後分別進行處理不就能夠了嗎

image.png

image.png

這裏咱們分別處理,一個是增長一個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就不會再變化,不論滾動與否,這時須要用戶主動點擊加載重試,這裏在點擊事件中拋出重試的通知,外部加載

image.png

這裏之因此有一個延時,目的是爲了防止setState太頻繁形成界面不變化的問題,理論上這裏大於16ms就能夠了


而後咱們回到捕獲處

image.png

後記

整體代碼只有300行,能夠在pub裏直接使用,目前最新版本爲0.1.1
pub地址 pub國內鏡像
github

歡迎issue 歡迎star

相關文章
相關標籤/搜索