Flutter 快速實(shí)現(xiàn)聊天會(huì)話列表效果示例詳解
一、目標(biāo)效果
聊天會(huì)話頁(yè)的列表效果
- 1、聊天數(shù)據(jù)不滿(mǎn)一屏?xí)r,頂部顯示所有聊天數(shù)據(jù)
- 2、插入消息時(shí)
- 如果最新消息緊靠列表底部時(shí),則插入消息會(huì)使列表向上推
- 如果不是緊靠列表底部,則固定到當(dāng)前聊天位置
效果如圖所示:

二、原理
1、 涉及的方法
ScrollPhysics 提供了 adjustPositionForNewDimensions 方法,用于修正 ScrollView 在 rebuild 后的偏移量,方法聲明如下
double adjustPositionForNewDimensions({
required ScrollMetrics oldPosition,
required ScrollMetrics newPosition,
required bool isScrolling,
required double velocity,
})
默認(rèn)情況下,值為上一次的偏移量,即 newPosition 參數(shù)的 pixels,所以在頂部插入消息時(shí),消息列表就會(huì)跟隨滾動(dòng)。
如下圖所示,觀察藍(lán)色的消息條目,每播入一條消息時(shí),所有消息會(huì)自動(dòng)往上頂,而滾動(dòng)視圖的偏移量其實(shí)一直是沒(méi)有變化的~

注:值得注意的是,如果該方法返回的值與 newPosition 的 pixels 不相等時(shí),則會(huì)觸發(fā)視圖的重新布局,所以這個(gè)操作還是比較昂貴的,應(yīng)盡量減少返回值的變動(dòng)。
2、實(shí)現(xiàn)邏輯
根據(jù)上述內(nèi)容我們不難推出插入消息時(shí)的效果實(shí)現(xiàn)原理如下:
| 效果 | 返回的值 |
|---|---|
| 消息緊靠列表底部時(shí),插入消息會(huì)使列表向上推 | 直接返回 super 的值,即 newPosition 參數(shù)的 pixels |
| 如果不是緊靠列表底部,則固定到當(dāng)前聊天位置 | 返回原本第 0 條消息的最新偏移量 |
下面重點(diǎn)說(shuō)明一下第 2 點(diǎn)中 返回原本第0條消息的最新偏移量 的實(shí)現(xiàn)邏輯:
ListView 的本質(zhì)是 RenderSliverList,通過(guò) RenderSliverList 的 firstChild 屬性拿到當(dāng)前列表中渲染的首個(gè) item。
如下圖,firstChild 是下標(biāo)為 10 的 item,這個(gè) item 與預(yù)渲染區(qū)域 cacheExtent 相關(guān),如果將其設(shè)置為 0,則 firstChild 的下標(biāo)將會(huì)是 12,這個(gè)相信不難理解。

所以,我們只需要在插入消息時(shí),記錄第 0 條消息的偏移量,當(dāng)列表視圖 rebuild 后,adjustPositionForNewDimensions 方法會(huì)被調(diào)用,此時(shí)取出第 1 條消息的偏移量,兩者的差值加上 super 的值即為目標(biāo)修正偏移量。
至于 聊天數(shù)據(jù)不滿(mǎn)一屏?xí)r,頂部顯示所有聊天數(shù)據(jù) 這個(gè)效果只是在切換 shrinkWrap 而已,比較簡(jiǎn)單就不在此展開(kāi)講了。
我已將上述邏輯進(jìn)行了封裝,集成于 flutter_scrollview_observer,接下來(lái)我們就來(lái)看看如何使用。
三、使用
現(xiàn)在只需三個(gè)步驟即可快速實(shí)現(xiàn)聊天會(huì)話列表的效果
步驟一:初始化必要的 ListObserverController 和 ChatScrollObserver
/// 初始化 ListObserverController
observerController = ListObserverController(controller: scrollController)
..cacheJumpIndexOffset = false;
/// 初始化 ChatScrollObserver
chatObserver = ChatScrollObserver(observerController)
..toRebuildScrollViewCallback = () {
// 這里可以重建指定的滾動(dòng)視圖即可
setState(() {});
};
步驟二:按如下配置 ListView 并使用 ListViewObserver 將其包裹
Widget _buildListView() {
Widget resultWidget = ListView.builder(
physics: ChatObserverClampinScrollPhysics(observer: chatObserver),
shrinkWrap: chatObserver.isShrinkWrap,
reverse: true,
controller: scrollController,
...
);
resultWidget = ListViewObserver(
controller: observerController,
child: resultWidget,
);
return resultWidget;
}
步驟三:插入或刪除消息前,調(diào)用 ChatScrollObserver 的 standby 方法
onPressed: () {
chatObserver.standby();
setState(() {
chatModels.insert(0, ChatDataHelper.createChatModel());
});
},
...
onRemove: () {
chatObserver.standby(isRemove: true);
setState(() {
chatModels.removeAt(index);
});
},
注:示例中的 setState 都可以換成對(duì)列表視圖進(jìn)行局部刷新的代碼
四、最后
GitHub地址: flutter_scrollview_observer
該庫(kù)還實(shí)現(xiàn)了其它十分實(shí)用的功能,相關(guān)功能有對(duì)應(yīng)的文章進(jìn)行敘述
以上就是Flutter 快速實(shí)現(xiàn)聊天會(huì)話列表效果示例詳解的詳細(xì)內(nèi)容,更多關(guān)于Flutter 聊天會(huì)話列表的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android實(shí)現(xiàn)簡(jiǎn)易計(jì)算功能
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)簡(jiǎn)易計(jì)算功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-06-06
Flutter使用AnimationController實(shí)現(xiàn)控制動(dòng)畫(huà)
這篇文章主要想帶大家來(lái)嘗試一下Flutter如何使用AnimationController實(shí)現(xiàn)一個(gè)拖拽圖片,然后返回原點(diǎn)的動(dòng)畫(huà),感興趣的可以了解一下2023-05-05
Kotlin中常見(jiàn)內(nèi)聯(lián)擴(kuò)展函數(shù)的使用方法教程
在Kotlin中,使用inline修飾符標(biāo)記內(nèi)聯(lián)函數(shù),既會(huì)影響到函數(shù)本身, 也影響到傳遞給它的Lambda表達(dá)式,這兩者都會(huì)被內(nèi)聯(lián)到調(diào)用處。下面這篇文章主要給大家介紹了關(guān)于Kotlin中常見(jiàn)內(nèi)聯(lián)擴(kuò)展函數(shù)的使用方法,需要的朋友可以參考下。2017-12-12
Android實(shí)現(xiàn)QQ圖片說(shuō)說(shuō)照片選擇效果
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)QQ圖片說(shuō)說(shuō)照片選擇效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-10-10
Android獲取屏幕方向及鍵盤(pán)狀態(tài)的小例子
很多開(kāi)發(fā)Android的網(wǎng)友可能需要判斷當(dāng)前的屏幕方向或鍵盤(pán)狀態(tài),下面的代碼可以判斷出橫屏landscape和常規(guī)的portrait縱握方式,如果使用的是G1這樣有QWERTY鍵盤(pán)硬件的,還可以判斷屏幕方向以及鍵盤(pán)的拉出狀態(tài)。2013-05-05

