SfListView ScrollTo Not Working Properly in MAUI Chat Application

We're experiencing persistent issues with the scrolling functionality in our MAUI chat application using SfListView. Specifically, the ListView fails to reliably scroll to the latest message when new messages are added.

Environment Details

  • Platform: .NET MAUI
  • OS: Android and iOS (tested on latest versions of both)
  • Syncfusion Package Version: 25.1.35
  • Visual Studio Version: 17.9.4

Issue Details

When new messages are added to our chat interface built with SfListView, the view does not automatically scroll down to show the latest messages. We're calling the ScrollTo method on the SfListView, but it's inconsistent in its behavior:

  1. The ScrollToLatest method is properly called, but the list doesn't scroll to the end
  2. The issue is more prominent when new messages are added and the keyboard is visible
  3. We've verified that our Messages collection is properly updating

Code Examples

We're using SfListView with the following configuration:

ChatView = new SfListView();
ChatView.ItemSpacing = 5;
ChatView.ItemTemplate = messageTemplateSelector;
ChatView.SelectionBackground = Brush.Transparent;
ChatView.AutoFitMode = AutoFitMode.DynamicHeight;
ChatView.ScrollBarVisibility = ScrollBarVisibility.Never;
ChatView.SelectionMode = Syncfusion.Maui.ListView.SelectionMode.None;
ChatView.SetBinding(SfListView.ItemsSourceProperty, nameof(ChatGPTModel.Messages));
Our ScrollToLatest method looks like this:
public void ScrollToLatest()
{
    if (ChatGPTModel?.Messages == null || ChatGPTModel.Messages.Count == 0)
        return;

    var scrollView = ChatView.GetScrollView();
    if (scrollView == null)
        return;
        
    var lastItem = ChatGPTModel.Messages[ChatGPTModel.Messages.Count - 1];
    ChatView.ScrollTo(lastItem, ScrollToPosition.End, false);
}

We call this method from our page's OnAppearing:

protected override void OnAppearing()
{
    try
    {
        base.OnAppearing();
#if ANDROID
        Platform.CurrentActivity?.Window?.SetSoftInputMode(Android.Views.SoftInput.AdjustResize);
#endif
        
        if (TabView?.SelectedIndex >= 0 && TabView.Items.Count > TabView.SelectedIndex)
        {
            if (TabView.Items[(int)TabView.SelectedIndex] is FCustomTabItem selectedTabItem &&
                selectedTabItem.Page is FChatGPT chatPage)
            {
                try
                {
                    chatPage.ScrollToLatest();
                    chatPage.FocusEntry();
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Debug.WriteLine($"Failed to scroll: {ex.Message}");
                }
            }
        }
    }
    catch (Exception e)
    {
        System.Diagnostics.Debug.WriteLine($"Exception in OnAppearing: {e.Message}");
    }
}

We also call it after adding messages to our collection:

public async Task SendMessage(string message)
{
    // Add user message to collection
    Messages.Add(new ChatMessage 
    { 
        Text = message,
        IsUserMessage = true 
    });
    
    // Try to scroll to show the message
    ConversationView?.ScrollToLatest();
    
    // API call and response handling...
    // ...
    
    // Add bot response to collection
    Messages.Add(new ChatMessage 
    { 
        Text = responseText,
        IsUserMessage = false 
    });
    
    // Try to scroll again after response
    ConversationView?.ScrollToLatest();
}

We've tried:

  1. Using both ScrollTo method and direct scrollView.ScrollToAsync calls
  2. Adding delays with await Task.Delay()
  3. Ensuring calls are on the UI thread
  4. Various combinations of the animated parameter (true/false)

But the scroll behavior remains unreliable.

Steps to Reproduce

  1. Add a SfListView to a MAUI page
  2. Populate it with chat message items
  3. Set up ScrollTo method to scroll to the latest item
  4. Call this method after adding new items to the collection
  5. Observe that the view doesn't reliably scroll to the latest item

Here's a minimal reproduction sample:

// In a page class
public partial class ChatPage : ContentPage
{
    private SfListView chatListView;
    private ObservableCollection<MessageItem> messages;
    private Entry messageEntry;

    public ChatPage()
    {
        messages = new ObservableCollection<MessageItem>();
        
        // Set up the list view
        chatListView = new SfListView
        {
            ItemsSource = messages,
            ItemSize = 200,
            ItemSpacing = 5,
            AutoFitMode = AutoFitMode.DynamicHeight,
            SelectionMode = SelectionMode.None,
            ItemTemplate = new DataTemplate(() => new MessageViewCell())
        };
        
        // Set up the input field
        messageEntry = new Entry { Placeholder = "Type a message" };
        Button sendButton = new Button { Text = "Send" };
        sendButton.Clicked += SendMessage;
        
        // Layout
        Grid inputGrid = new Grid
        {
            ColumnDefinitions =
            {
                new ColumnDefinition { Width = GridLength.Star },
                new ColumnDefinition { Width = GridLength.Auto }
            }
        };
        inputGrid.Add(messageEntry, 0, 0);
        inputGrid.Add(sendButton, 1, 0);
        
        Content = new Grid
        {
            RowDefinitions =
            {
                new RowDefinition { Height = GridLength.Star },
                new RowDefinition { Height = GridLength.Auto }
            },
            Children =
            {
                { chatListView, 0, 0 },
                { inputGrid, 0, 1 }
            }
        };
    }
    
    private void SendMessage(object sender, EventArgs e)
    {
        if (string.IsNullOrWhiteSpace(messageEntry.Text))
            return;
            
        // Add user message
        messages.Add(new MessageItem 
        { 
            Text = messageEntry.Text, 
            IsUser = true 
        });
        
        // Attempt to scroll to the last item
        ScrollToLatest();
        
        // Simulate bot response after 1 second
        string userMessage = messageEntry.Text;
        messageEntry.Text = "";
        
        Task.Delay(1000).ContinueWith(t => 
        {
            // Add bot message
            MainThread.BeginInvokeOnMainThread(() => 
            {
                messages.Add(new MessageItem 
                { 
                    Text = $"You said: {userMessage}", 
                    IsUser = false 
                });
                
                // Attempt to scroll again - this often fails
                ScrollToLatest();
            });
        });
    }
    
    public void ScrollToLatest()
    {
        if (messages.Count == 0) return;
        
        var lastItem = messages[messages.Count - 1];
        chatListView.ScrollTo(lastItem, ScrollToPosition.End, false);
        
        // We've also tried:
        // var scrollView = chatListView.GetScrollView();
        // if (scrollView != null)
        // {
        //    scrollView.ScrollToAsync(0, scrollView.ContentSize.Height, true);
        // }
    }
}

public class MessageItem
{
    public string Text { get; set; }
    public bool IsUser { get; set; }
}

public class MessageViewCell : ViewCell
{
    public MessageViewCell()
    {
        var label = new Label
        {
            Padding = new Thickness(10),
            TextColor = Colors.White
        };
        
        label.SetBinding(Label.TextProperty, "Text");
        
        var frame = new Frame
        {
            CornerRadius = 10,
            Content = label
        };
        
        var grid = new Grid();
        grid.Children.Add(frame);
        
        // Position based on sender
        var isUserBinding = new Binding("IsUser");
        frame.SetBinding(Frame.BackgroundColorProperty, 
            new Binding("IsUser", converter: new BoolToColorConverter()));
        frame.SetBinding(Frame.HorizontalOptionsProperty, 
            new Binding("IsUser", converter: new BoolToOptionsConverter()));
        
        View = grid;
    }
}

// Converters
public class BoolToColorConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (bool)value ? Colors.Blue : Colors.Gray;
    }
    
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

public class BoolToOptionsConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (bool)value ? LayoutOptions.End : LayoutOptions.Start;
    }
    
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}



3 Replies

RM RiyasHameed MohamedAbdulKhader Syncfusion Team April 22, 2025 07:02 AM UTC

Hi Nguyen Thinh,
We have reviewed your reported query and prepared a simple sample based on the code snippet you provided. It appears that the ListView scrolls to the newly added messages as expected. For your reference, we have attached the tested video.
Could you please share the version of .NET MAUI you are using?
Additionally, if possible, we kindly request you to modify the attached sample to reproduce the issue. This will greatly help us investigate the problem further and provide an appropriate solution as quickly as possible.

Regards,
Riyas Hameed M


Attachment: ListViewDemo_1d75e761.zip


TV Thijs van Rijswijk replied to RiyasHameed MohamedAbdulKhader March 2, 2026 05:36 AM UTC

Good morning,

Same problem with ScrollTo. In the code-behind I have:

public partial class Trip : ContentPage
{
readonly TripViewModel _viewModel;

public Trip(TripViewModel viewModel)
{
InitializeComponent();
BindingContext = _viewModel = viewModel;
_viewModel.TripCollectionView = this.MyListView;
}
}

In my ViewModel I have two private methods: SetScrollIndex, which calculates which trip I selected, and ScrollTo, which, when returning to the page, scrolls to the current trip.

private void SetScrollIndex()
{
int count = 0;
foreach (TripDto dto in TripCollection!)
{
if (dto.Tripnr == _tripState!.GetTripDto()?.Tripnr)
break;
else
count++;
}
_tripState.SetScrollIndex(count);
}

private void ScrollTo()
{
TripCollectionView!.SelectedItem = null;
MainThread.BeginInvokeOnMainThread(() =>
{
TripCollectionView!.ScrollTo(
_tripState.GetScrollIndex(),
ScrollToPosition.Start,
true
);
});
}

Both with MainThread and without invoking it, the list jumps to an unpredictable trip.
The first time ScrollTo is called, the ScrollIndex is 0.

ScrollTo is only called in OnAppearing().
I call SetScrollIndex, for example, when navigating to another page and store the value in a kind of state handler so the value is preserved.

Any idea what could be causing this?

Kind regards,
Thijs van Rijswijk



MM Muthukumar Madasamy Syncfusion Team March 3, 2026 01:21 PM UTC

Hi Thijs van Rijswijk,

Thank you for reaching out and sharing the details.

We have noticed that you are using an index with the ScrollTo method. In the Syncfusion SfListView, the ScrollTo API expects either:
  • The data object to scroll to, or
  • A specific pixel position (for example, 1000) when using position-based scrolling,
not the index directly.

If you want to scroll based on the item index, please use the ScrollToRowIndex method instead of ScrollTo. This will ensure the list scrolls to the correct item.

For more details, please refer to our documentation on programmatic scrolling:

We have also checked this behavior and attached a sample for your reference. Please try this approach at your end and let us know whether the issue is resolved. If the issue still persists, kindly reproduce the problem in the shared sample and send it back to us. This will help us investigate more accurately and provide a precise solution.

Best Regards,
Muthu Kumar M.

Attachment: ListViewMaui_c59c8238.zip

Loader.
Up arrow icon