-
-
Notifications
You must be signed in to change notification settings - Fork 46
3、Chat Observer
We only need three steps to implement the chat session page effect.
- 1、All chat data are displayed at the top of the listView when there is less than one screen of chat data.
- 2、When inserting a chat data
- If the latest message is close to the bottom of the list, the listView will be pushed up.
- Otherwise, the listView will be fixed to the current chat position.
Step 1: Initialize the necessary ListObserverController
and ChatScrollObserver
.
/// Initialize ListObserverController
observerController = ListObserverController(controller: scrollController)
..cacheJumpIndexOffset = false;
/// Initialize ChatScrollObserver
chatObserver = ChatScrollObserver(observerController)
// Greater than this offset will be fixed to the current chat position.
..fixedPositionOffset = 5
..toRebuildScrollViewCallback = () {
// Here you can use other way to rebuild the specified listView instead of [setState]
setState(() {});
};
Step 2: Configure ListView
as follows and wrap it with ListViewObserver
.
Widget _buildListView() {
Widget resultWidget = ListView.builder(
physics: ChatObserverClampingScrollPhysics(observer: chatObserver),
shrinkWrap: chatObserver.isShrinkWrap,
reverse: true,
controller: scrollController,
...
);
resultWidget = ListViewObserver(
controller: observerController,
child: resultWidget,
);
return resultWidget;
}
Step 3: Call the [standby] method of ChatScrollObserver
before inserting or removing chat data.
onPressed: () {
chatObserver.standby();
setState(() {
chatModels.insert(0, ChatDataHelper.createChatModel());
});
},
...
onRemove: () {
chatObserver.standby(isRemove: true);
setState(() {
chatModels.removeAt(index);
});
},
This feature only handles inserting one message by default. If you need to insert multiple messages at once, you can pass the changeCount
parameter to the standby
method.
_addMessage(int count) {
chatObserver.standby(changeCount: count);
setState(() {
needIncrementUnreadMsgCount = true;
for (var i = 0; i < count; i++) {
chatModels.insert(0, ChatDataHelper.createChatModel());
}
});
}
Note: This feature relies on the latest message view before the message is inserted as a reference to calculate the offset, so if too many messages are inserted at once and the reference message view cannot be rendered, this feature will fail, and you need to try to avoid this problem by setting a reasonable value for cacheExtent
of ScrollView
by yourself!
The size calculation of cacheExtent can refer to the following formula:
cacheExtent = [maximum height of a message] * [number of messages inserted at one time] + 200
chatObserver = ChatScrollObserver(observerController)
..onHandlePositionResultCallback = (result) {
switch (result.type) {
case ChatScrollObserverHandlePositionType.keepPosition:
// Keep the current chat position.
// updateUnreadMsgCount(changeCount: result.changeCount);
break;
case ChatScrollObserverHandlePositionType.none:
// Do nothing about the chat position.
// updateUnreadMsgCount(isReset: true);
break;
}
};
This callback is mainly used to display the unread bubbles of new messages when adding chat messages.
The generative messages like ChatGPT
also need to keep the message position when looking through old messages, you only need to adjust the processing mode in the standby
method.
chatObserver.standby(
mode: ChatScrollObserverHandleMode.generative,
// changeCount: 1,
);
Note: The referenced item
will be determined internally based on changeCount
, and this mode only supports the case where generative messages are continuous.
If your generative messages are discontinuous, or there are generative message updates and the behavior of adding and deleting messages at the same time, in this complex case, you need to specify the referenced item
by yourself, and This processing mode is more flexible.
chatObserver.standby(
mode: ChatScrollObserverHandleMode.specified,
refIndexType: ChatScrollObserverRefIndexType.relativeIndexStartFromCacheExtent,
refItemIndex: 2,
refItemIndexAfterUpdate: 2,
);
- Set
mode
to.specified
. - Set
refItemIndex
to relative index of the referenceditem
before the update. - Set
refItemIndexAfterUpdate
to relative index of the referenceditem
after the update.
Note: The functions of refItemIndex
and refItemIndexAfterUpdate
differ based on the value of refIndexType
, as shown below
enum ChatScrollObserverRefIndexType {
/// relativeIndex trailing
///
/// 6 | item16 | cacheExtent
/// ----------------- -----------------
/// 5 | item15 |
/// 4 | item14 |
/// 3 | item13 | displaying
/// 2 | item12 |
/// 1 | item11 |
/// ----------------- -----------------
/// 0 | item10 | cacheExtent <---- start
///
/// leading
relativeIndexStartFromCacheExtent,
/// relativeIndex trailing
///
/// 5 | item16 | cacheExtent
/// ----------------- -----------------
/// 4 | item15 |
/// 3 | item14 |
/// 2 | item13 | displaying
/// 1 | item12 |
/// 0 | item11 | <---- start
/// ----------------- -----------------
/// -1 | item10 | cacheExtent
///
/// leading
relativeIndexStartFromDisplaying,
/// Directly specify the index of item.
itemIndex,
}
Remember, your refItemIndex
and refItemIndexAfterUpdate
should point to the same message object whatever you set!