Jetpack Compose: Moving up and down multiple multi-line BasicTextFields with physical keyboard

441 Views Asked by At

I noticed that if I have multiple multi-line BasicTextField in a Column, if one the text fields has focus and I press the up and down arrow keys on my physical keyboard (using Android Studio emulator), the focus is moved to a different text field instead of going up or down to a different line of text within the text field.

I kind of like the idea of letting the up and down arrow keys change focus, but only if the cursor is at the first or last line.

I came up with a solution, but I am wondering if there is an easier way to do this. Also, if I want to allow a change of focus using arrow keys AND through other methods, it seems I would have to modify this somehow to check the key that was pressed with one of those key modifiers, but I am not sure the best way to do that. Do you know how to determine if a key press is also triggering focus movement/change?

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Column {
                repeat(5) {
                    BasicTextFieldWithText()
                }
            }
        }
    }
}
@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun BasicTextFieldWithText() {
    var text by remember {
        mutableStateOf(
            TextFieldValue("BasicTextField\n" + "with multiple lines\n" + "of text")
        )
    }

    var textLayout by remember {
        mutableStateOf<TextLayoutResult?>(null)
    }

    val atFirstLine by remember {
        derivedStateOf {
            textLayout?.let {
                val range = text.selection
                range.collapsed && it.getLineForOffset(range.end) == 0
            } ?: false
        }
    }

    val atLastLine by remember {
        derivedStateOf {
            textLayout?.let {
                val range = text.selection
                range.collapsed && it.getLineForOffset(range.end) == (it.lineCount - 1)
            } ?: false
        }
    }

    BasicTextField(
        value = text,
        onValueChange = { text = it },
        onTextLayout = { textLayout = it },
        modifier = Modifier
            .border(1.dp, Color.Blue)
            .padding(8.dp)
            .focusProperties {
                up = if (atFirstLine) FocusRequester.Default else FocusRequester.Cancel
                down = if (atLastLine) FocusRequester.Default else FocusRequester.Cancel
            }
    )
}
0

There are 0 best solutions below