Zum Hauptinhalt springen
tsecurity.de LIVE
Echtzeit-Radar & Feeds
Alle RSS Feeds
👥 Community & Social
Sichere ProgrammierungChrome Already Has The Eyedropper You're Building(20.09.2026 um 18:25 Uhr)
Sichere ProgrammierungFor VS Code lovers, you can have a colored border and more from now...(20.09.2026 um 18:25 Uhr)
Sichere ProgrammierungNuxt Hydration Mismatch: Why It Happens and How to Fix It(20.09.2026 um 18:26 Uhr)
Sichere ProgrammierungYour Browser Is Rejecting Every Drop On Purpose(20.09.2026 um 18:26 Uhr)
Sichere ProgrammierungReact Derived State: Why That useState Is Probably a Bug(20.09.2026 um 18:27 Uhr)
Sichere ProgrammierungI tried OpenProject and Vikunja. Then I built Agila.(20.09.2026 um 18:37 Uhr)
Sichere ProgrammierungSkill Recorder keeps your screen local until you press Analyze(20.09.2026 um 18:38 Uhr)
Sichere ProgrammierungChrome Already Has The Eyedropper You're Building(20.09.2026 um 18:25 Uhr)
Sichere ProgrammierungFor VS Code lovers, you can have a colored border and more from now...(20.09.2026 um 18:25 Uhr)
Sichere ProgrammierungNuxt Hydration Mismatch: Why It Happens and How to Fix It(20.09.2026 um 18:26 Uhr)
Sichere ProgrammierungYour Browser Is Rejecting Every Drop On Purpose(20.09.2026 um 18:26 Uhr)
Sichere ProgrammierungReact Derived State: Why That useState Is Probably a Bug(20.09.2026 um 18:27 Uhr)
Sichere ProgrammierungI tried OpenProject and Vikunja. Then I built Agila.(20.09.2026 um 18:37 Uhr)
Sichere ProgrammierungSkill Recorder keeps your screen local until you press Analyze(20.09.2026 um 18:38 Uhr)
Intelligence View
⚡ tsecurity.de Intelligence

How to create LazyColumn with drag and drop elements in Jetpack Compose. Part 1.

Reagiere als Erste:r — dein Feedback zählt!

Hello there! The subject of drag and drop elements isn't new and you can find many decisions of this problem in the Internet. But the decision isn't so obviously like a creating drag and drop lists by RecyclerView. And I suppose my step-by-step guide will be useful.

Sources:

If you just want to solve your problem you can get code from LazyColumnDragAndDropDemo.kt and implement it to your project.

So, I'm getting a solution from Make it Easy: How to implement Drag and Drop List Item in Jetpack Compose. I'll try to explain step-by-step what happens. Next, I'll solve issues and do refactoring. At the end of refactoring I wanna get something like LazyColumnDragAndDropDemo.kt.

At the beginning I wanna say that the most interesting will be in next parts. You can think of this part as a preview.

OK, let's get started.

About start commit of app

Starter code
So, we have three layers in our app:

  • UI - consists of one screen with two buttons (Add user and Clear list) and class AppViewModel and data class UserEntityUi
  • Domain - consists of data class UserEntity and interface UserRepository
  • Data - consists of database (created by Room) and class UserRepositoryImpl

Also we have Hilt for DI.

ViewModel is subscribed on changes of Flow<List<UserEntity>> and the screen collectAsState() this StateFlow. nothing much...

Step 1

First of all we need to add a reaction on gestures

LazyColumn(
    modifier = Modifier
        .fillMaxSize()
        .weight(1f)
        .pointerInput(Unit) {
            detectDragGesturesAfterLongPress(
                onDrag = { },
                onDragStart = { },
                onDragEnd = { },
                onDragCancel = { }
            )
        }
)

And reaction we add by Modifier.pointerInput and detectDragGesturesAfterLongPress. You can read about an understanding gestures in Understand gestures | Google Developers by yourself. I just don't want to copy text of official guides and documentation it's not correctly.

Step 2

As usual in Compose we need some state for drag and drop item. Definitely inside this state we need to have LazyListState and some action for moving element onMove: (Int, Int) -> Unit.
OK, let's create it.

class DragAndDropListState(
    val lazyListState: LazyListState,
    private val onMove: (Int, Int) -> Unit
)

And of course we need to remember our state.

@Composable
fun rememberDragAndDropListState(
    lazyListState: LazyListState,
    onMove: (Int, Int) -> Unit
): DragAndDropListState {
    return remember { DragAndDropListState(lazyListState, onMove) }
}

And let's create some extension for moving in a mutableList

fun <T> MutableList<T>.move(from: Int, to: Int) {
    if (from == to) return
    val element = this.removeAt(from)
    this.add(to, element)
}

Next let's inject them into our screen

is UiState.Success -> {
    val users = state.data.toMutableStateList()
    val dragAndDropListState =
        rememberDragAndDropListState(lazyListState) { from, to ->
            users.move(from, to)
        }
/* LazyColumn etc. */
}

toMutableStateList() function create SnapshotStateList<T> that can be observed and snapshot.

Step 3

So, we see 4 params in detectDragGesturesAfterLongPress which we should implement: onDrag, onDragStart, pnDragEnd, onDragCancel. Go back to our class DragAndDropListState and start...

onStart

The first of all we need to know which element we dragged. It needs some variables:

private var initialDraggingElement by mutableStateOf<LazyListItemInfo?>(null)
var currentIndexOfDraggedItem by mutableStateOf<Int?>(null)

And now we can implement onDragStart function for setting initial values of current element.
But first let's create an extension to getting offset of end of element:

private val LazyListItemInfo.offsetEnd: Int
    get() = this.offset + this.size
fun onDragStart(offset: Offset) {
    lazyListState.layoutInfo.visibleItemsInfo
        .firstOrNull { item -> offset.y.toInt() in item.offset..item.offsetEnd }
        ?.also {
            initialDraggingElement = it
            currentIndexOfDraggedItem = it.index
        }
}

So, let's paste onStart() into LazyColumn

LazyColumn(
    modifier = Modifier
        .fillMaxSize()
        .weight(1f)
        .pointerInput(Unit) {
            detectDragGesturesAfterLongPress(
                onDrag = { },
                onDragStart = { offset ->
                    dragAndDropListState.onDragStart(offset)
                },
                onDragEnd = { },
                onDragCancel = { }
            )
        }
){
/*Composable content*/      
}

onDrag

What do we wanna do?

  • we want to move an element along the Y coordinate
  • when we moved an element to the place of another element it needs to change indexes of both elements

OK, let's create a variable for distance of offset

private var draggingDistance by mutableFloatStateOf(0f)
fun onDrag(offset: Offset) {
    draggingDistance += offset.y
}

Next we need to get offsets of our initial element

private var draggingDistance by mutableFloatStateOf(0f)
private val initialOffsets: Pair<Int, Int>?
    get() = initialDraggingElement?.let { Pair(it.offset, it.offsetEnd) }

fun onDrag(offset: Offset) {
    draggingDistance += offset.y

    initialOffsets?.let { (top, bottom) ->
        val startOffset = top.toFloat() + draggingDistance
        val endOffset = bottom.toFloat() + draggingDistance
    }
}

Next I gonna directly get the element. In LazyList (LazyColumn or LazyRow) index of element isn't equal a position on a screen. So, we need some extension for getting the element by index.

private fun LazyListState.getVisibleItemInfo(itemPosition: Int): LazyListItemInfo? {
    return this.layoutInfo.visibleItemsInfo.getOrNull(itemPosition - this.firstVisibleItemIndex)
    }
private val currentElement: LazyListItemInfo?
    get() = currentIndexOfDraggedItem?.let {
        lazyListState.getVisibleItemInfo(it)
    }

Next our task is changing indexes - we have to change the index of element to which we moved the dragged element, and we have to change index of dragged element.

fun onDrag(offset: Offset) {
    draggingDistance += offset.y

    initialOffsets?.let { (top, bottom) ->
        val startOffset = top.toFloat() + draggingDistance
        val endOffset = bottom.toFloat() + draggingDistance

        currentElement?.let { current ->
            lazyListState.layoutInfo.visibleItemsInfo
                .filterNot { item ->
                    item.offsetEnd < startOffset || item.offset > endOffset || current.index == item.index
                }
                .firstOrNull { item ->
                    val delta = startOffset - current.offset
                    when {
                        delta < 0 -> item.offset > startOffset
                        else -> item.offsetEnd < endOffset
                    }
                }
        }?.also { item ->
            currentIndexOfDraggedItem?.let { current ->
                onMove.invoke(current, item.index)
            }
            currentIndexOfDraggedItem = item.index
        }
    }
}

onDrag function is completed. Let's paste it to LazyColumn.

detectDragGesturesAfterLongPress(
    onDrag = { change, offset ->
        change.consume()
        dragAndDropListState.onDrag(offset)

/*some code below*/

But it's not all because if we try to use it we can see that list doesn't scroll behind dragged item. For scrolling list we have to add over scroll checker and some CoroutineJob for the .scrollBy() function.

Add over scroll checker function to class DragAndDropListState

fun checkOverscroll(): Float {
    return initialDraggingElement?.let {
        val startOffset = it.offset + draggingDistance
        val endOffset = it.offsetEnd + draggingDistance

        return@let when {
            draggingDistance > 0 -> {
                (endOffset - lazyListState.layoutInfo.viewportEndOffset).takeIf { diff -> diff > 0 }

            }

            draggingDistance < 0 -> {
                (startOffset - lazyListState.layoutInfo.viewportStartOffset).takeIf { diff -> diff < 0 }
            }

            else -> null
        }
    } ?: 0f
}

Add Coroutine Scope and Coroutine Job to the top of list

val coroutineScope = rememberCoroutineScope()
var overscrollJob by remember { mutableStateOf<Job?>(null) }

And add scrollBy() to onDrag

onDrag = { change, offset ->
    change.consume()
    dragAndDropListState.onDrag(offset)

    if (overscrollJob?.isActive == true) return@detectDragGesturesAfterLongPress

    dragAndDropListState
        .checkOverscroll()
        .takeIf { it != 0f }
        ?.let {
            overscrollJob = coroutineScope.launch {
                dragAndDropListState.lazyListState.scrollBy(it)
            }
        } ?: kotlin.run { overscrollJob?.cancel() }

}

onDragEnd, onDragCancel

Here we just reset variables in class DragAndDropListState

fun onDragInterrupted() {
    initialDraggingElement = null
    currentIndexOfDraggedItem = null
    draggingDistance = 0f
}

Step 4

There is one thing what we have to do. We should define the modifier of element.

Let's create a variable inside class DragAndDropListState

val elementDisplacement: Float?
    get() = currentIndexOfDraggedItem?.let {
        lazyListState.getVisibleItemInfo(it)
    }?.let { itemInfo ->
        (initialDraggingElement?.offset ?: 0f).toFloat() + draggingDistance - itemInfo.offset
    }

And our element's modifier looks like this:

ItemCard(
    userEntityUi = user,
    modifier = Modifier
        .composed {
            val offsetOrNull =
                dragAndDropListState.elementDisplacement.takeIf {
                    index == dragAndDropListState.currentIndexOfDraggedItem
                }
            Modifier.graphicsLayer {
                translationY = offsetOrNull ?: 0f
            }
        }
)

Preliminary results

Now we have the same code like in Make it Easy: How to implement Drag and Drop List Item in Jetpack Compose video.

You can get the code by this link: Part 1 code

And this even works :)
Working example

Issues

  • If we add a new user our list back to the starter order of elements. It happens we add user to a database, from the database we receive a new users list, obviously our uiState also have a new value, next recomposition.
  • If we add a new user and try to interact with last elements we can catch Index Of Bounds Exception. It happens because we have no any keys for update DragAndDropListState.

Refactoring and resolving issues will be in next Part 2 (place for link).

Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten How to create LazyColumn with drag and drop elements in Jetpack Compose. Part 1.

Thematisch verwandte Begriffe: create, LazyColumn, with, drag · 6 Treffer

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-93956 | A flaw has been found in olivier-ls PHP-FTS up to 1.1.2. Affected by thi…
Advisory →
TTS Reader • tsecurity.de Voice
tsecurity.de Icon
tsecurity.de App
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag
Themen-Radar & Intelligence Matrix
Echtzeit-Taxonomie nach Angriffsvektoren & Plattformen
Community Radar & Live Chat
Sentinel Bot online • Live-Stream
Dein Cluster: Security Explorer
Match:
lädt…
Verbindung zum Community-Stream wird aufgebaut...
Bearbeitungsmodus — Senden überschreibt deine Nachricht
Community-Puls — was gerade passiert
lädt…
Aktivitäten deiner Analysten
lädt…
Neues Thema oder Eilmeldung einreichen

Reiche interessante Links, Zero-Days oder Debatten ein. Die Community entscheidet per Upvote über die Veröffentlichung.

Heiß diskutierte Einreichungen
🔖 Gespeicherte Artikel
📂 Keine gespeicherten Artikel vorhanden.
Zurück Ziehen Vor
Links: vorheriger Artikel Rechts: nächster Artikel unten: schließen
News NIS-2 Frühwarnung Tier-1 Intel ⏱️ 3 Min vor 10 Min
Artikeldaten werden geladen...

Zurück: vorheriger Vor: nächster
↗ Original-Quelle
Social Reaktionen Deine Reaktion zählt
Einstufung & Relevanz-Poll 0 Stimmen
In sozialen Netzwerken teilen 1-Klick