I’m new in Jetpack Compose, I’m trying to do a drag and drop screen with items to be moved from a column to another one. I took inspiration from this link and I creted the following files:
TaskColumns.kt
@Preview
@Composable
fun TaskColumns() {
var columnWidth by remember {
mutableStateOf(0.dp)
}
val density = LocalDensity.current
val items = remember {
mutableListOf(
mutableListOf(5,6,7,8),
mutableListOf(),
mutableListOf(),
mutableListOf())
}
Surface(
Modifier.fillMaxSize()
) {
LongPressDraggable {
Row(Modifier.padding(4.dp)) {
repeat(4){
val column = it
DropTarget<IntArray>(modifier = Modifier.weight(1f)) { a, data ->
val n = data?.get(0)
val i = data?.get(1)
val c = data?.get(2)
var color = if (a) Color.Green
else Color.Blue
if (i != null && n != null && c != null) {
if (c != column){
Log.d("DND", "${data[0]}, ${data[1]}")
items[column].add(n)
items[c].remove(n)
}
color = Color.Blue
}
Column(
Modifier
.fillMaxHeight()
.fillMaxWidth()
.padding(4.dp)
.background(color)
.onPlaced {
columnWidth = with(density) { it.size.width.toDp() }
}
) {
items[column].forEachIndexed { index, item ->
DragTarget(
modifier = Modifier,
dataToDrop = intArrayOf(item, index, column),
onDragCustomAction = {
Log.d("DND", "$item")
}
) {
Column(
Modifier
.size(columnWidth)
.aspectRatio(1f / 1f)
.background(Color.Black),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(text = "$item", color = Color.White)
}
}
Spacer(modifier = Modifier.height(8.dp))
}
}
}
}
}
}
}
}
DragAndDrop.kt
internal val LocalDragTargetInfo = compositionLocalOf { DragTargetInfo() }
@Composable
fun LongPressDraggable(
modifier: Modifier = Modifier,
content: @Composable BoxScope.() -> Unit
) {
val state = remember { DragTargetInfo() }
CompositionLocalProvider(
LocalDragTargetInfo provides state
) {
Box(modifier = modifier.fillMaxSize(), contentAlignment = Alignment.TopStart)
{
content()
if (state.isDragging) {
var targetSize by remember {
mutableStateOf(IntSize.Zero)
}
Box(modifier = Modifier
.graphicsLayer {
val offset = (state.dragPosition + state.dragOffset)
// scaleX = 1.3f
// scaleY = 1.3f
alpha = if (targetSize == IntSize.Zero) 0f else .9f
translationX = offset.x.minus(targetSize.width / 2)
translationY = offset.y.minus(targetSize.height / 2)
}
.onGloballyPositioned {
targetSize = it.size
}
) {
state.draggableComposable?.invoke()
}
}
}
}
}
@Composable
fun <T> DragTarget(
modifier: Modifier,
dataToDrop: T,
onDragCustomAction: ()->Unit = {},
onDragCancelCustomAction: ()->Unit = {},
content: @Composable (() -> Unit),
) {
var currentPosition by remember { mutableStateOf(Offset.Zero) }
val currentState = LocalDragTargetInfo.current
Box(modifier = modifier
.onGloballyPositioned {
currentPosition = it.localToWindow(Offset.Zero)
}
.pointerInput(Unit) {
detectDragGesturesAfterLongPress(onDragStart = {
currentState.dataToDrop = dataToDrop
currentState.isDragging = true
currentState.dragPosition = currentPosition + it
currentState.draggableComposable = content
}, onDrag = { change, dragAmount ->
change.consume()
currentState.dragOffset += Offset(dragAmount.x, dragAmount.y)
onDragCustomAction()
}, onDragEnd = {
currentState.isDragging = false
currentState.dragOffset = Offset.Zero
}, onDragCancel = {
onDragCancelCustomAction()
currentState.dragOffset = Offset.Zero
currentState.isDragging = false
})
}) {
content()
}
}
@Composable
fun <T> DropTarget(
modifier: Modifier,
content: @Composable() (BoxScope.(isInBound: Boolean, data: T?) -> Unit)
) {
val dragInfo = LocalDragTargetInfo.current
val dragPosition = dragInfo.dragPosition
val dragOffset = dragInfo.dragOffset
var isCurrentDropTarget by remember {
mutableStateOf(false)
}
Box(modifier = modifier.onGloballyPositioned {
it.boundsInWindow().let { rect ->
isCurrentDropTarget = rect.contains(dragPosition + dragOffset)
}
}) {
val data =
if (isCurrentDropTarget && !dragInfo.isDragging) dragInfo.dataToDrop as T? else null
content(isCurrentDropTarget, data)
}
}
internal class DragTargetInfo {
var isDragging: Boolean by mutableStateOf(false)
var dragPosition by mutableStateOf(Offset.Zero)
var dragOffset by mutableStateOf(Offset.Zero)
var draggableComposable by mutableStateOf<(@Composable () -> Unit)?>(null)
var dataToDrop by mutableStateOf<Any?>(null)
}
With this code I should be able to move a black square from a column to the other one. The problem is that if I move the first item for example, it works, but then if I try to move the second one (that now became the first in the list) the value displayed is the same of the previous item.
You can see an example in this gif
The problem seems to be the value passed to dataToDrop in DragTarget dowsn’t update. What am I doing wrong?