Jetpack Compose: Drag-and-drop reorder for lists

I would like to stop whatever I am working on and just re-write all my Android apps using Jetpack Compose. At the time of writing, this might not be the best decision for all apps.

You see, Compose is indeed in stable form (i.e. performance is great and APIs won't change) but there are some features that you would otherwise expect but are not yet implemented.

While porting one of my apps, I stumbled upon such a feature: drag-and-drop reordering for lists.

There's an active feature request and there's drag-and-drop support in the roadmap but no official workaround at the moment. A quick search reveals some manual implementations of the feature. But in my opinion, the best workaround at the moment is the ComposeReorderable library that implements the feature while abstracting away all the implementation details.

The drag-and-drop effect might not be perfect but it gets the job done until there's an official solution. Let's see with a quick code snippet how to accomplish this.

    val state = rememberReorderState() // 1.
    val tasks by viewModel.getTasks().observeAsState(listOf())
    
    LazyColumn(
        state = state.listState,
        modifier = Modifier
            .reorderable(state, { fromPos, toPos -> // 2.
                viewModel.onTaskReordered(tasks, fromPos, toPos)
            })
            .detectReorderAfterLongPress(state) // 3.
    ) {
        items(tasks, key = { task -> task.id }) { task ->
            Text(
                task.text,
                Modifier
                    .draggedItem(state.offsetByKey(task.id)) // 4.
            )
        }
    }

0. Install the library by adding the following to your build.gradle (remember to check for the latest version) :

 implementation("org.burnoutcrew.composereorderable:reorderable:0.7.0")
  1. The state in a normal lazy list is holding the scroll position of the list. The library needs its own state for performing the drag-and-drop reordering.
  2. Add the reorderable modifier to specify what happens when two items in a list change places while dragging. In our case, following a unidirectional architecture, the ViewModel gets notified about the change to update the internal state and subsequently update the view.
  3. This detectReorderAfterLongPress modifier can be declared either on the entire list or specific list items. If declared on the list, all the items are reorderable. If declared on specific items, only those are reorderable (with the rest of them being immovable).
  4. This is the default visual effect provided by the library when dragging the items in the list.

In my opinion, this is a very decent workaround on having drag-and-drop reordering for a list in Jetpack Compose right now. When the official way for having reorderable lists is available, we can have a look again.

Happy coding!