The best examples of Kotlin RecyclerView – 3 practical use cases for modern Android apps

If you’re building a modern Android app in 2024, you will hit RecyclerView sooner rather than later. And when you search for **examples of Kotlin RecyclerView examples – 3 practical use cases**, you usually get the same tired demo of a static list of strings. That’s not how real apps work. You need patterns you can actually ship to production: paginated feeds, interactive lists, and layouts that don’t crumble when the product team changes the design. In this guide, we’ll walk through three practical use cases that go beyond the toy sample. Each example of RecyclerView in Kotlin is built around real-world scenarios: a chat-style list with multiple view types, a paginated feed powered by the Paging 3 library, and a drag-and-drop list for reordering items. Along the way, we’ll look at adapter patterns, DiffUtil, click handling, and how this still matters even with Jetpack Compose gaining ground. If you’re hunting for real examples of Kotlin RecyclerView that match 2024 expectations, this is for you.
Written by
Jamie
Published
Updated

Why RecyclerView still matters in 2024

Jetpack Compose is getting most of the headlines, but RecyclerView is very much alive in production apps. Many teams are:

  • Maintaining large legacy codebases that depend on RecyclerView.
  • Shipping hybrid UIs where fragments and activities still host RecyclerView-based screens.
  • Using RecyclerView for advanced layouts (carousels, grids, drag-and-drop) that still feel more familiar than Compose equivalents.

So learning from examples of Kotlin RecyclerView is still a smart investment. The best examples don’t just show how to display a list; they show how to manage state, performance, and interaction patterns you see in real apps.


Use Case 1: Chat-style list with multiple view types

If you’re looking for examples of Kotlin RecyclerView examples – 3 practical use cases that feel like real products, chat UIs are at the top of the list. Almost every messaging screen uses some form of RecyclerView with multiple view types.

Core idea

You have a list of messages where each item can be:

  • A message sent by the user
  • A message received from another user
  • A system event ("User joined”, “Message deleted")

This is a classic example of multiple view types in a single RecyclerView.

Data model

sealed class ChatItem {
    data class OutgoingMessage(
        val id: Long,
        val text: String,
        val timestamp: Long
    ) : ChatItem()

    data class IncomingMessage(
        val id: Long,
        val senderName: String,
        val text: String,
        val timestamp: Long
    ) : ChatItem()

    data class SystemEvent(
        val id: Long,
        val text: String,
        val timestamp: Long
    ) : ChatItem()
}

Adapter with multiple view types

class ChatAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    private val items = mutableListOf<ChatItem>()

    companion object {
        private const val TYPE_OUTGOING = 1
        private const val TYPE_INCOMING = 2
        private const val TYPE_SYSTEM = 3
    }

    fun submitList(newItems: List<ChatItem>) {
        items.clear()
        items.addAll(newItems)
        notifyDataSetChanged()
    }

    override fun getItemViewType(position: Int): Int = when (items[position]) {
        is ChatItem.OutgoingMessage -> TYPE_OUTGOING
        is ChatItem.IncomingMessage -> TYPE_INCOMING
        is ChatItem.SystemEvent -> TYPE_SYSTEM
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        val inflater = LayoutInflater.from(parent.context)
        return when (viewType) {
            TYPE_OUTGOING -> {
                val view = inflater.inflate(R.layout.item_outgoing_message, parent, false)
                OutgoingViewHolder(view)
            }
            TYPE_INCOMING -> {
                val view = inflater.inflate(R.layout.item_incoming_message, parent, false)
                IncomingViewHolder(view)
            }
            else -> {
                val view = inflater.inflate(R.layout.item_system_event, parent, false)
                SystemViewHolder(view)
            }
        }
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        when (val item = items[position]) {
            is ChatItem.OutgoingMessage -> (holder as OutgoingViewHolder).bind(item)
            is ChatItem.IncomingMessage -> (holder as IncomingViewHolder).bind(item)
            is ChatItem.SystemEvent -> (holder as SystemViewHolder).bind(item)
        }
    }

    override fun getItemCount(): Int = items.size

    class OutgoingViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        fun bind(item: ChatItem.OutgoingMessage) {
            // bind views here
        }
    }

    class IncomingViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        fun bind(item: ChatItem.IncomingMessage) {
            // bind views here
        }
    }

    class SystemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        fun bind(item: ChatItem.SystemEvent) {
            // bind views here
        }
    }
}

This pattern is one of the best examples of how RecyclerView can handle rich, heterogeneous content without becoming unmanageable.

Other real examples that use this pattern

Real-world screens that mirror this approach include:

  • Notification centers mixing different notification types.
  • Activity feeds with posts, comments, and system badges.
  • E-commerce order timelines (order placed, shipped, delivered, refunded).

All of these are examples of Kotlin RecyclerView where multiple view types keep your code organized and your UI flexible.


Use Case 2: Paginated feed with Paging 3 and Kotlin coroutines

Infinite scrolling feeds are everywhere: social media, product catalogs, search results. When people ask for examples of Kotlin RecyclerView examples – 3 practical use cases, a paginated feed is almost always on the list.

For 2024 and beyond, the recommended approach is Paging 3 with Kotlin coroutines and Flow.

PagingSource implementation

class ArticlesPagingSource(
    private val api: ArticlesApi
) : PagingSource<Int, Article>() {

    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Article> {
        return try {
            val page = params.key ?: 1
            val response = api.getArticles(page = page, pageSize = params.loadSize)

            LoadResult.Page(
                data = response.items,
                prevKey = if (page == 1) null else page - 1,
                nextKey = if (response.items.isEmpty()) null else page + 1
            )
        } catch (e: Exception) {
            LoadResult.Error(e)
        }
    }

    override fun getRefreshKey(state: PagingState<Int, Article>): Int? {
        return state.anchorPosition?.let { anchor ->
            val anchorPage = state.closestPageToPosition(anchor)
            anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1)
        }
    }
}

Pager and Flow in a repository

class ArticlesRepository(private val api: ArticlesApi) {

    fun getArticlesStream(): Flow<PagingData<Article>> {
        return Pager(
            config = PagingConfig(
                pageSize = 20,
                enablePlaceholders = false,
                prefetchDistance = 2
            ),
            pagingSourceFactory = { ArticlesPagingSource(api) }
        ).flow
    }
}

PagingDataAdapter for RecyclerView

class ArticlesAdapter(
    private val onArticleClick: (Article) -> Unit
) : PagingDataAdapter<Article, ArticlesAdapter.ArticleViewHolder>(DIFF_CALLBACK) {

    companion object {
        private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<Article>() {
            override fun areItemsTheSame(oldItem: Article, newItem: Article): Boolean =
                oldItem.id == newItem.id

            override fun areContentsTheSame(oldItem: Article, newItem: Article): Boolean =
                oldItem == newItem
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ArticleViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.item_article, parent, false)
        return ArticleViewHolder(view, onArticleClick)
    }

    override fun onBindViewHolder(holder: ArticleViewHolder, position: Int) {
        getItem(position)?.let { holder.bind(it) }
    }

    class ArticleViewHolder(
        itemView: View,
        private val onArticleClick: (Article) -> Unit
    ) : RecyclerView.ViewHolder(itemView) {

        fun bind(article: Article) {
            // bind views here
            itemView.setOnClickListener { onArticleClick(article) }
        }
    }
}

Collecting data in a Fragment

@AndroidEntryPoint
class ArticlesFragment : Fragment(R.layout.fragment_articles) {

    private val viewModel: ArticlesViewModel by viewModels()
    private val adapter = ArticlesAdapter { article ->
        // handle clicks
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        val recyclerView = view.findViewById<RecyclerView>(R.id.recyclerView)
        recyclerView.adapter = adapter
        recyclerView.layoutManager = LinearLayoutManager(requireContext())

        viewLifecycleOwner.lifecycleScope.launch {
            viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.articles.collectLatest { pagingData ->
                    adapter.submitData(pagingData)
                }
            }
        }
    }
}

This is one of the best examples of a modern Kotlin RecyclerView setup: reactive, paginated, and production-ready.

Other real examples that mirror this pattern

Other examples include:

  • Product lists in shopping apps with endless scroll.
  • Search result screens that load more as you scroll.
  • News feeds or blog lists pulling from a REST API.
  • User directories or follower lists in social apps.

All of these are reliable examples of Kotlin RecyclerView integrated with Paging 3 and coroutines.


Use Case 3: Drag-and-drop reordering with ItemTouchHelper

Another example of RecyclerView that shows up in real apps is drag-and-drop reordering. Think of apps where users can:

  • Reorder playlists.
  • Prioritize tasks in a to-do list.
  • Customize home screen sections or dashboards.

This is where ItemTouchHelper comes in.

Basic setup

class ReorderableAdapter(
    private val items: MutableList<String>
) : RecyclerView.Adapter<ReorderableAdapter.ViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.item_reorderable, parent, false)
        return ViewHolder(view)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.bind(items[position])
    }

    override fun getItemCount(): Int = items.size

    fun moveItem(fromPosition: Int, toPosition: Int) {
        if (fromPosition == toPosition) return
        val moved = items.removeAt(fromPosition)
        items.add(toPosition, moved)
        notifyItemMoved(fromPosition, toPosition)
    }

    class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        fun bind(text: String) {
            // bind views here
        }
    }
}

ItemTouchHelper callback

class DragManageAdapter(
    private val adapter: ReorderableAdapter
) : ItemTouchHelper.Callback() {

    override fun getMovementFlags(
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder
    ): Int {
        val dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN
        val swipeFlags = 0
        return makeMovementFlags(dragFlags, swipeFlags)
    }

    override fun onMove(
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder,
        target: RecyclerView.ViewHolder
    ): Boolean {
        adapter.moveItem(viewHolder.bindingAdapterPosition, target.bindingAdapterPosition)
        return true
    }

    override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
        // No swipe support here
    }

    override fun isLongPressDragEnabled(): Boolean = true
}

Attaching to RecyclerView

val adapter = ReorderableAdapter(mutableListOf("One", "Two", "Three"))
recyclerView.adapter = adapter
recyclerView.layoutManager = LinearLayoutManager(context)

val callback = DragManageAdapter(adapter)
val touchHelper = ItemTouchHelper(callback)
touchHelper.attachToRecyclerView(recyclerView)

This pattern is a practical example of Kotlin RecyclerView for interactive UIs where the user is in control of ordering.

Other real examples using drag-and-drop

You’ll see similar behavior in:

  • Music apps letting users reorder songs in a queue.
  • Note-taking apps where sections can be rearranged.
  • Settings screens where quick actions can be reordered.

These are all examples of RecyclerView providing a more tactile, user-driven experience.


Six more real examples of Kotlin RecyclerView in production-style UIs

To go beyond the three main scenarios, here are more real examples you’ll encounter often:

  • A horizontal carousel of featured items on a home screen using LinearLayoutManager with HORIZONTAL orientation.
  • A grid of photos or products using GridLayoutManager, with span sizes adjusted for featured items.
  • A settings screen where sections and toggles are all backed by a single RecyclerView with multiple view types.
  • A favorites list with swipe-to-delete using ItemTouchHelper for swipe gestures only.
  • A calendar-like list of events grouped by date, using sticky headers implemented as another view type.
  • A download manager screen that updates progress bars in each item as downloads progress via Flow or LiveData.

Each of these is an example of how flexible RecyclerView is when combined with Kotlin, coroutines, and the rest of the Jetpack stack.


RecyclerView vs. Jetpack Compose in 2024–2025

You can’t talk about examples of Kotlin RecyclerView in 2024 without acknowledging Jetpack Compose. Google’s own guidance, as of 2024, is that Compose is the recommended path for new UI development, but RecyclerView is still widely supported and documented.

Some teams are:

  • Keeping existing RecyclerView screens and writing new ones in Compose.
  • Embedding Compose inside RecyclerView items using ComposeView for gradual migration.
  • Using RecyclerView for very large lists where they already have tuned performance and caching.

The Android docs on Compose interoperability show how both approaches can coexist. So learning from solid examples of Kotlin RecyclerView examples – 3 practical use cases still pays off, even if your long-term strategy is Compose.


FAQ: Kotlin RecyclerView examples

What are the most common examples of Kotlin RecyclerView in real apps?

Some of the best examples include chat lists, infinite feeds with Paging 3, drag-and-drop task lists, swipe-to-delete email lists, and grid-based galleries. All of them share the same building blocks: a RecyclerView, a Kotlin-based adapter, and a layout manager.

Can you show a simple example of a Kotlin RecyclerView adapter?

class SimpleStringAdapter(
    private val items: List<String>
) : RecyclerView.Adapter<SimpleStringAdapter.ViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.item_simple_text, parent, false)
        return ViewHolder(view)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.bind(items[position])
    }

    override fun getItemCount(): Int = items.size

    class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        fun bind(text: String) {
            // bind text to TextView
        }
    }
}

While basic, this is often the first example of Kotlin RecyclerView that beginners implement before moving to more advanced patterns.

Is RecyclerView still worth learning with Jetpack Compose available?

Yes. Many organizations have large RecyclerView-based codebases that will be around for years. Hiring managers still expect Android developers to understand RecyclerView, DiffUtil, and adapters. Knowing modern examples of Kotlin RecyclerView also makes it easier to reason about how lists work in Compose, because the underlying performance considerations are similar.

Where can I find official guidance and patterns?

For official Android patterns and updated guidance, check:

  • Android’s RecyclerView docs: https://developer.android.com/guide/topics/ui/layout/recyclerview
  • Android Architecture Components and Paging: https://developer.android.com/topic/libraries/architecture/paging

These references complement the examples of Kotlin RecyclerView examples – 3 practical use cases in this article with deeper API details and best practices.

Explore More Kotlin Code Snippets

Discover more examples and insights in this category.

View All Kotlin Code Snippets