I'm having a list of messages and after getting all the previous messages or after sending a new message, I want my ListView to get scrolled to the bottom.
I have created a ScrollController
:
final ScrollController _scrollController = ScrollController();
a listener:
void _scrollListener() {
// Checks if it needs to be scrolled or will get OOR
if (_scrollController.offset >=
_scrollController.position.maxScrollExtent &&
!_scrollController.position.outOfRange) {}
}
and a scrollToBottom()
:
void _scrollToBottom() {
// Scrolls to bottom if needed
WidgetsBinding.instance.addPostFrameCallback((_) {
if (_scrollController.hasClients) {
_scrollController.animateTo(
_scrollController.position.maxScrollExtent,
duration: const Duration(milliseconds: 300),
curve: Curves.easeOut,
);
}
});
}
and I'm calling the scrollToBottom()
in my sendMessage()
, fetchPreviousMessages()
and initState()
:
void _sendMessage(String message) async {
// Sends message to the WebSocket
final userData = await _preferencesService.getPreferences();
if (message.isNotEmpty) {
MessageRequest messageRequest = MessageRequest(
message: _textFieldController.text,
username: userData.username,
roomID: widget.room.uniqueID);
_channel.sink.add(jsonEncode(messageRequest.toJson()));
}
// clear textfield
_textFieldController.clear();
_scrollToBottom();
}
void _fetchPreviousMessages() async {
// get all previous messages for the room
final websocketService = WebSocketService();
await websocketService
.fetchPreviousMessage(widget.room.uniqueID)
.then((messages) {
for (var i = 0; i < messages.length; i++) {
_messages.add(messages[i]);
setState(() {
_isLoading = false;
}); //hide loader
}
_scrollToBottom();
});
@override
void initState() {
super.initState();
// get all previous messages for the room
_fetchPreviousMessages();
// init WebSocket
_initWebSocket();
// init the scroll controlled
_scrollController.addListener(_scrollListener);
_scrollToBottom();
// init textField listened in order to make button unavailable
_textFieldController.addListener(() {
bool isChatButtonActive = _textFieldController.text.isNotEmpty;
});
}
Sometimes it does work and scrolls to bottom, but sometimes it scrolls only halfway or not at all and I cannot understand why...
Thanks in advance!
Just a workaround but it should work as expected. Use clamping physics in the list view. Add an extra number to the max extent
_scrollController.animateTo(
_scrollController.position.maxScrollExtent+300,
duration: const Duration(
milliseconds: 200,
),
curve: Curves.easeInOut,
);