一、思路回顧
首先,在聊天區域整體是一個可滾動的界面,我們的需求是通過用戶下拉手勢,即可獲取新的聊天記錄,并加在原聊天記錄的頂端,給用戶制造一種無感連接的體驗,加載或者加載完畢都要對用戶進行資訊的提示,
即可以將思路分為以下幾步:
1、在有限的高度中,加載二十條聊天記錄,形成可以滾動的區域
2、滑動至滾動區域的【頂部】時,呼叫觸發獲取新的20條訊息的介面,且該程序中頂部有提示文字,“正在加載中…”
3、介面獲取資料完畢后,對資料進行必要的處理后,將資料拼接至原資料之中,頂部且有充足的位置留給新給的資料,如果再無新的20條訊息,則將提示文字改成“已加載顯示全部訊息”,
但是以上以一個正常的ListView來實作,便會出現一個問題,當新的資料出現時,將資料通過insertAll方法插入至第0條之前的位置時,便會出現新的資料將舊的資料頂替的效果,至于為什么會出現這一點,我估計是因為我們滑動到頂部時,我們并沒有為頂部至第一條訊息之間留出一個新的距離,導致滾動距離還停留在頂部,但是資料的后二十條已經變成了新的二十條,所以會出現一個【替換】效果,但是我們需要的是一個拼接效果,
于是我在網路上找到了解決方案(畢竟不是我自己想出來的…),找到的時候還是覺得,還是大家聰明啊,,,,但是我還是想自己再捋一捋整個思路
原文地址:https://cloud.tencent.com/developer/article/1647244
二、正確且最終的實作思路是:
使用Listview,并且flutter的串列滾動還帶有了reverse的屬性,也就是可以將整個串列進行旋轉顛倒,這樣我們的底部變成了頂部,頂部其實是串列的底部,在新的資料進行拼接時,就會自動在下面留出一部分距離,也就不會造成替換的效果,
具體思路:
1、當整體歷史訊息不為空時回傳可以滾動的區域,使用ScrollConfiguration包裹Listview,以便實作自己想要的行為,
2、對Listview進行reverse,且itemcount為資料長度+1,留出空位用于顯示加載提示資訊
3、在資料長度+1 的位置(我們看到的頂部)加載提示資訊,其余位置加載訊息體
4、對滾動進行監聽,當滾動位置大于當前最大滾動距離時,修改加載狀態,
5、根據加載狀態對加載提示資訊進行單獨的控制
整個ListView的代碼如下:
Widget buildMessageListContainer() {
if (_historyMessageList.length == 0) {
return Container(
height: Adapt.px(50),
alignment: Alignment.center,
child: Text(
'暫無歷史訊息',
style: TextStyle(color: Colors.grey, fontSize: Adapt.px(13)),
));
} else {
return Column(
children: [
Expanded(
child: ScrollConfiguration(
// color: Colors.green,
behavior: ChatScrollBehavior(),
child: ListView.builder(
controller: _scrollController,
reverse: true,
physics: ChatScrollPhysics(
parent: AlwaysScrollableScrollPhysics()),
itemCount: _historyMessageList.length + 1,
itemBuilder: (BuildContext context, int index) {
if (index == _historyMessageList.length) {
return loadingView();
} else {
return getMessageRow(index);
}
}),
),
),
SizedBox(
height: INPUT_CONTAINER_HEIGHT,
)
],
);
}
}
ChatScrollPhysics和ChatScrollBehavior是原博主為了優化滾動效果和行為對方法進行了重寫,
ChatScrollPhysics:目的是為了實作下滑加載帶彈性效果,上滑屏蔽彈性效果
ChatScrollBehavior:使用ScrollConfiguration包裹滑動組件behavior設定成自己實作的behavior,
滾動監聽:
void scrollListener() {
if (_scrollController!.position.pixels >=
_scrollController!.position.maxScrollExtent) {
_getMoreData();
}
}
_scrollController!.addListener(scrollListener);
獲取新資料且改變加載提示資訊
void _getMoreData() async {
if (_loadStatus == LoadingStatus.STATUS_IDLE) {
_loadStatus = LoadingStatus.STATUS_LOADING;
loadText = '加載中...';
List moreList = [];
var sentTime =
this._historyMessageList[_historyMessageList.length - 1].sentTime;
var list = await RongImSdk.getHistoryMessage(_groupId, sentTime, 20, 0);
if (list.length > 0) {
//去掉第一條,因為第一條已經顯示
for (int i = 1; i < list.length; i++) {
moreList.add(list[i]);
}
if (moreList.length > 0) {
_loadStatus = LoadingStatus.STATUS_IDLE;
this._historyMessageList.addAll(moreList);
} else {
_loadStatus = LoadingStatus.STATUS_COMPLETED;
loadText = '已顯示全部訊息';
}
} else {
_loadStatus = LoadingStatus.STATUS_COMPLETED;
loadText = '已顯示全部訊息';
}
setState(() {});
}
}
加載提示資訊樣式:
Widget loadingView() {
var loadingTs =
TextStyle(color: Constants.ITEM_LABEL_COLOR_999, fontSize: 12);
var loadingText = Padding(
padding: EdgeInsets.only(left: Adapt.px(0)),
child: Text(
loadText,
style: loadingTs,
),
);
var loadingIndicator = SizedBox(
child: CircularProgressIndicator(
strokeWidth: Adapt.px(2),
valueColor: AlwaysStoppedAnimation(Colors.blue)),
width: Adapt.px(12),
height: Adapt.px(12),
);
return Padding(
padding: EdgeInsets.only(top: Adapt.px(20), bottom: Adapt.px(20)),
child: Row(
children: <Widget>[
_loadStatus == LoadingStatus.STATUS_LOADING
? loadingIndicator
: Text(''),
loadingText
],
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
));
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/385640.html
標籤:其他
