androidkotlinandroid-jetpack-composepopupkeyboard-focus

Getting keyboard focus in OutlinedTextField inside a Popup with Jetpack Compose


I have the function below which is a Popup wrapping an OutlinedTextField. This function is called the "main" Composable function when boolean variable is true, and the variable is set by a FloatingActionButton.

@Composable
private fun NewObject(
    popupWidth: Float,
    popupHeight:Float,
    focusRequester: FocusRequester,
    onClickOutside: () -> Unit,
)
{
    var field by remember { mutableStateOf("") }

    Popup(
        alignment = Alignment.Center,
        onDismissRequest = { onClickOutside() },
    ) {
        Column(
            Modifier
                .width(popupWidth.dp)
                .height(popupHeight.dp)
                .background(Color.White)
                .clip(RoundedCornerShape(4.dp))
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            OutlinedTextField(value = field,
                onValueChange = { field = it },
                label = { Text("Enter text") },
                modifier = Modifier
                    .focusRequester(focusRequester)
            )
        }
    }
}

@Composable
private fun ManageVehicle() {
    
    var popupVisible by remember { mutableStateOf(false) }
    val focusRequester = remember { FocusRequester() }

    if (popupVisible) {
        AddNewVehicle(500.0F, 500.0F, showPopup = false, onClickOutside = {
            popupVisible = false
        }, focusRequester = focusRequester)
    }

    Scaffold(
        topBar = {
            TopAppBar(title = { Text(stringResource(R.string.action_vehicle)) })
        },
        floatingActionButton = {
            FloatingActionButton(
                onClick = {
                    popupVisible = true
//  This causes an error "java.lang.IllegalStateException: FocusRequester is not initialized"
//                    focusRequester.requestFocus()
                },
                containerColor = MaterialTheme.colorScheme.primary,
                contentColor = Color.White,
                shape = CircleShape,
                content = {
                    Icon(
                        imageVector = Icons.TwoTone.Add,
                        contentDescription = "Add FAB",
                        tint = Color.White,
                    )
                }
            )
        },
        modifier = Modifier.fillMaxSize()
    )

However, the OutlinedTextField never gets keyboard focus, even if I use a FocusRequester. The focus always stays with the composables defined in the "main" function. What am I doing wrong?


Solution

  • Here is what you can do to get the desired results,

    1. add popup properties to the popup and set Focusable to true.
    2. add the focus requested to the popups launched effect
    3. the fab should just handle the visibility of the focus
    @Composable
    private fun NewObject(
        popupWidth: Float,
        popupHeight: Float,
        focusRequester: FocusRequester,
        onClickOutside: () -> Unit,
    ) {
        var field by remember { mutableStateOf("") }
        var interactionSource = MutableInteractionSource()
    
        Popup(
            properties = PopupProperties(focusable = true, dismissOnBackPress = true),
            alignment = Alignment.Center,
            onDismissRequest = { onClickOutside() },
        ) {
            Column(
                modifier = Modifier
                    .width(popupWidth.dp)
                    .height(popupHeight.dp)
                    .background(Color.White)
                    .clip(RoundedCornerShape(4.dp)),
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                OutlinedTextField(
                    value = field,
                    onValueChange = { field = it },
                    label = { Text("Enter text") },
                    interactionSource = interactionSource,
                    modifier = Modifier
                        .focusRequester(focusRequester)
                )
            }
    
            LaunchedEffect(key1 = Unit) {
                focusRequester.requestFocus()
            }
        }
    }```
    
    The Fab callback :
    
    ```onClick = {popupVisible = true }```