A modifier that allows for receiving from a drag and drop gesture.
TextDragAndDropTargetSample
@Sampled
@Composable
fun TextDragAndDropTargetSample(
eventSummary: String?,
onDragAndDropEventDropped: (DragAndDropEvent) -> Unit,
) {
val validMimeTypePrefixes = remember {
setOf(ClipDescription.MIMETYPE_TEXT_INTENT, "image/", "text/", "video/", "audio/")
}
var backgroundColor by remember { mutableStateOf(Color.Transparent) }
val dragAndDropTarget = remember {
object : DragAndDropTarget {
override fun onStarted(event: DragAndDropEvent) {
backgroundColor = Color.DarkGray.copy(alpha = 0.2f)
}
override fun onDrop(event: DragAndDropEvent): Boolean {
onDragAndDropEventDropped(event)
return true
}
override fun onEnded(event: DragAndDropEvent) {
backgroundColor = Color.Transparent
}
}
}
Box(
modifier =
Modifier.fillMaxSize()
.dragAndDropTarget(
shouldStartDragAndDrop = accept@{ startEvent ->
val hasValidMimeType =
startEvent.mimeTypes().any { eventMimeType ->
validMimeTypePrefixes.any(eventMimeType::startsWith)
}
hasValidMimeType
},
target = dragAndDropTarget,
)
.background(backgroundColor)
.border(width = 4.dp, color = Color.Magenta, shape = RoundedCornerShape(16.dp))
) {
when (eventSummary) {
null -> Text(modifier = Modifier.align(Alignment.Center), text = "Drop anything here")
else ->
Text(
modifier =
Modifier.padding(horizontal = 16.dp, vertical = 24.dp)
.verticalScroll(rememberScrollState()),
text = eventSummary,
)
}
}
}
private fun DragAndDropEvent.summary() =
(0 until toAndroidDragEvent().clipData.itemCount)
.map(toAndroidDragEvent().clipData::getItemAt)
.withIndex()
.joinToString(separator = "\n\n") { (index, clipItem) ->
val mimeTypes =
(0 until toAndroidDragEvent().clipData.description.mimeTypeCount).joinToString(
separator = ", ",
transform = toAndroidDragEvent().clipData.description::getMimeType,
)
listOfNotNull(
"index: $index",
"mimeTypes: $mimeTypes",
clipItem.text?.takeIf(CharSequence::isNotEmpty)?.let { "text: $it" },
clipItem.htmlText?.takeIf(CharSequence::isNotEmpty)?.let { "html text: $it" },
clipItem.uri?.toString()?.let { "uri: $it" },
clipItem.intent?.let {
"intent: action - ${it.action}; extras size: ${it.extras?.size()}"
},
)
.joinToString(separator = "\n")
}