
Drag-and-drop reorderable grid with multi-span items, dense or order-preserving packing, four reorder modes (swap/insert hover/drop), animated transitions, auto-scroll, configurable columns and spacing.
A drag-and-drop reorderable grid component for Compose Multiplatform. Supports multi-span items, dense packing, and multiple reorder modes.
Targets: Android, iOS (arm64 / simulatorArm64), Desktop (JVM)
Add the dependency to your build.gradle.kts:
// build.gradle.kts
dependencies {
implementation("com.devpulsar.ui:densespangrid:1.0.1")
}@Composable
fun MyGrid() {
var items by remember { mutableStateOf(listOf("A", "B", "C", "D", "E")) }
DenseSpanGrid(
items = items,
columns = 3,
modifier = Modifier.fillMaxSize(),
key = { it },
onMove = { from, to ->
items = items.toMutableList().apply {
add(to, removeAt(from))
}
}
) { item, isDragging ->
Card(
modifier = Modifier.fillMaxSize(),
elevation = if (isDragging) 8.dp else 2.dp
) {
Text(item, modifier = Modifier.padding(16.dp))
}
}
}Items can span multiple rows and columns using GridSpan:
data class MyItem(val id: Int, val title: String, val span: GridSpan)
val items = listOf(
MyItem(1, "Small", GridSpan(1, 1)),
MyItem(2, "Wide", GridSpan(1, 2)),
MyItem(3, "Tall", GridSpan(2, 1)),
MyItem(4, "Large", GridSpan(2, 2)),
)
DenseSpanGrid(
items = items,
columns = 3,
key = { it.id },
spanProvider = { it.span },
onMove = { from, to -> /* reorder logic */ }
) { item, isDragging ->
// item content
}| Parameter | Type | Default | Description |
|---|---|---|---|
items |
List<T> |
required | Items to display |
columns |
Int |
required | Number of grid columns |
modifier |
Modifier |
Modifier |
Layout modifier |
key |
((T) -> Any)? |
null |
Unique key provider for stable reordering |
spanProvider |
(T) -> GridSpan |
GridSpan.Single |
Span size per item |
horizontalSpacing |
Dp |
8.dp |
Horizontal gap between items |
verticalSpacing |
Dp |
8.dp |
Vertical gap between items |
contentPadding |
PaddingValues |
PaddingValues(0.dp) |
Padding around the grid |
rowHeight |
Dp |
100.dp |
Height of a single row |
packingMode |
PackingMode |
Dense |
Layout packing strategy |
reorderMode |
ReorderMode |
SwapOnHover |
Drag-and-drop behavior |
dragStartTimeout |
Long |
500L |
Long-press duration (ms) to start drag |
enableVerticalScroll |
Boolean |
true |
Enable built-in vertical scrolling |
onAutoScroll |
((Float) -> Unit)? |
null |
Callback for external scroll integration |
onMove |
((Int, Int) -> Unit)? |
null |
Called with (fromIndex, toIndex) on reorder |
itemContent |
@Composable (T, Boolean) -> Unit |
required | Item renderer (item, isDragging) |
| Mode | Description |
|---|---|
Dense |
Fills gaps by placing items in the first available slot |
PreserveOrder |
Places items sequentially, preserving list order |
| Mode | Description |
|---|---|
SwapOnHover |
Swaps dragged item with target while hovering |
SwapOnDrop |
Swaps dragged item with target on drop |
InsertOnHover |
Inserts dragged item at target position while hovering |
InsertOnDrop |
Inserts dragged item at target position on drop |
# Compile library
./gradlew :library:build
# Run desktop tests
./gradlew :library:desktopTest
# Run sample desktop app
./gradlew :sample:runThis project is licensed under the MIT License.
A drag-and-drop reorderable grid component for Compose Multiplatform. Supports multi-span items, dense packing, and multiple reorder modes.
Targets: Android, iOS (arm64 / simulatorArm64), Desktop (JVM)
Add the dependency to your build.gradle.kts:
// build.gradle.kts
dependencies {
implementation("com.devpulsar.ui:densespangrid:1.0.1")
}@Composable
fun MyGrid() {
var items by remember { mutableStateOf(listOf("A", "B", "C", "D", "E")) }
DenseSpanGrid(
items = items,
columns = 3,
modifier = Modifier.fillMaxSize(),
key = { it },
onMove = { from, to ->
items = items.toMutableList().apply {
add(to, removeAt(from))
}
}
) { item, isDragging ->
Card(
modifier = Modifier.fillMaxSize(),
elevation = if (isDragging) 8.dp else 2.dp
) {
Text(item, modifier = Modifier.padding(16.dp))
}
}
}Items can span multiple rows and columns using GridSpan:
data class MyItem(val id: Int, val title: String, val span: GridSpan)
val items = listOf(
MyItem(1, "Small", GridSpan(1, 1)),
MyItem(2, "Wide", GridSpan(1, 2)),
MyItem(3, "Tall", GridSpan(2, 1)),
MyItem(4, "Large", GridSpan(2, 2)),
)
DenseSpanGrid(
items = items,
columns = 3,
key = { it.id },
spanProvider = { it.span },
onMove = { from, to -> /* reorder logic */ }
) { item, isDragging ->
// item content
}| Parameter | Type | Default | Description |
|---|---|---|---|
items |
List<T> |
required | Items to display |
columns |
Int |
required | Number of grid columns |
modifier |
Modifier |
Modifier |
Layout modifier |
key |
((T) -> Any)? |
null |
Unique key provider for stable reordering |
spanProvider |
(T) -> GridSpan |
GridSpan.Single |
Span size per item |
horizontalSpacing |
Dp |
8.dp |
Horizontal gap between items |
verticalSpacing |
Dp |
8.dp |
Vertical gap between items |
contentPadding |
PaddingValues |
PaddingValues(0.dp) |
Padding around the grid |
rowHeight |
Dp |
100.dp |
Height of a single row |
packingMode |
PackingMode |
Dense |
Layout packing strategy |
reorderMode |
ReorderMode |
SwapOnHover |
Drag-and-drop behavior |
dragStartTimeout |
Long |
500L |
Long-press duration (ms) to start drag |
enableVerticalScroll |
Boolean |
true |
Enable built-in vertical scrolling |
onAutoScroll |
((Float) -> Unit)? |
null |
Callback for external scroll integration |
onMove |
((Int, Int) -> Unit)? |
null |
Called with (fromIndex, toIndex) on reorder |
itemContent |
@Composable (T, Boolean) -> Unit |
required | Item renderer (item, isDragging) |
| Mode | Description |
|---|---|
Dense |
Fills gaps by placing items in the first available slot |
PreserveOrder |
Places items sequentially, preserving list order |
| Mode | Description |
|---|---|
SwapOnHover |
Swaps dragged item with target while hovering |
SwapOnDrop |
Swaps dragged item with target on drop |
InsertOnHover |
Inserts dragged item at target position while hovering |
InsertOnDrop |
Inserts dragged item at target position on drop |
# Compile library
./gradlew :library:build
# Run desktop tests
./gradlew :library:desktopTest
# Run sample desktop app
./gradlew :sample:runThis project is licensed under the MIT License.