Search code examples
androidandroid-jetpack-compose

Prevent BasicTextField from being focused on longpress


How can I prevent a Jetpack Compose BasicTextField to get focused on long press? I want it to get focused on a normal tap. But not by a long press or a swipe gesture, for example. I am using the newest version of BasicTextField.

                    BasicTextField(
                        value = TextFieldValue(
                            text = "TEST TEST",
                            selection = TextRange("TEST TEST".length)
                        ),
                        onValueChange = { newText ->  },
                        singleLine = true,
                        textStyle = MaterialTheme.typography.body1
                    )

To give some context on why I need this: The BasicTextField is part of a lazylist item. Each item can be reordered by longpressing it. Each item can also be deleted by a swipe to dismiss gesture. So I don't want these user inputs to trigger the edit mode of the text field.

I already tried all the answers from ChatGPT, Claude and so on. Trying to ignore the longpress with emtpy pointerInput modifiers does not work. I also played around with pointerInteropFilter and focusRequester, but to no avail.


Solution

  • i think there is no direct way to do it so this might be helpful if it fits your usecase.

    @Composable
    fun DraggableTextField(
        initialText: String = "Custom text",
        longPressDurationMs: Long = 300,
    ) {
        var textFieldValue by remember { mutableStateOf(TextFieldValue(initialText)) }
        val interactionSource = remember { MutableInteractionSource() }
        val isPressed by interactionSource.collectIsPressedAsState()
        var canFocus by remember { mutableStateOf(true) }
        var pressStartTime by remember { mutableStateOf<Long?>(null) }
        var position by remember { mutableStateOf(Offset.Zero) }
    
       // Handle press duration logic
       LaunchedEffect(isPressed) {
            if (isPressed) {
                pressStartTime = System.currentTimeMillis()
            } else {
                pressStartTime?.let { startTime ->
                    val duration = System.currentTimeMillis() - startTime
                    if (duration > longPressDurationMs) {
                        canFocus = false
                    } else {
                        canFocus = true
                    }
                }
                pressStartTime = null
            }
        }
    
        // making text selection appears transparent 
        val customTextSelectionColors = remember {
            TextSelectionColors(
                handleColor = Color.Transparent,
                backgroundColor = Color.Transparent
            )
        }
    
        CompositionLocalProvider(
            LocalTextSelectionColors provides customTextSelectionColors,
            LocalTextToolbar provides EmptyTextToolbar
        ) {
            BasicTextField(
                value = textFieldValue,
                onValueChange = { textFieldValue = it },
                interactionSource = interactionSource,
                modifier = Modifier
                    .graphicsLayer(
                        translationX = position.x,
                        translationY = position.y
                    )
                    .fillMaxWidth()
                    .background(Color.Cyan)
                    .padding(vertical = 5.dp)
                    .focusProperties { this.canFocus = canFocus },
                    //decoration box needs to be used in order to be able to detect drag gesture
                    decorationBox = { innerTextField ->
                    Row(
                        Modifier
                            .pointerInput(Unit) {
                                detectDragGestures { change, dragAmount ->
                                    change.consume()
                                    position += dragAmount
                            }
                            }
                            .border(1.dp, Color.Gray, shape = RectangleShape)
                            .padding(16.dp)
                            .fillMaxWidth().
                    ) {
                        innerTextField()
                    }
                }
            )
        }
    }
    
    //to remove copy/paste toolbar that appears on long press
    object EmptyTextToolbar: TextToolbar {
       override val status: TextToolbarStatus = TextToolbarStatus.Hidden
    
       override fun hide() {  }
    
       override fun showMenu(
           rect: Rect,
           onCopyRequested: (() -> Unit)?,
           onPasteRequested: (() -> Unit)?,
           onCutRequested: (() -> Unit)?,
          onSelectAllRequested: (() -> Unit)?,
       ) {
       }
    }