Thomas
02/18/2025, 1:11 PMESC
on Desktop and DPAD_BACK
on Android TV no longer behave as expected in BasicTextField
and TextField
. Previously, I handled these events manually to control focus and navigation, but now they seem to trigger an automatic back action, breaking focus management.
The changelog mentions PredictiveBackHandler
but only refers to iOS. Does this also affect Desktop? If so, is there a way to:
1. Access and control this new BackHandler API?
2. Disable it selectively for input fields?
This is critical for proper navigation and text input handling. Any insights would be appreciated!
Thanks!Alexander Maryanovsky
02/18/2025, 3:39 PMThomas
02/19/2025, 12:06 AM@Composable
fun AppNavHost() {
val navController = rememberNavController()
NavHost(
navController = navController,
graph = createNavGraph(navController),
modifier = Modifier.fillMaxSize(),
)
}
fun createNavGraph(navController: NavHostController): NavGraph {
return navController.createGraph(startDestination = "screenA") {
composable("screenA") {
ScreenA (onNavigateToB = { navController.navigate("screenB") })
}
composable("screenB") {
ScreenB (onNavigateBack = { navController.popBackStack() })
}
}
}
Thomas
02/19/2025, 12:06 AM@Composable
fun ScreenA(onNavigateToB: () -> Unit) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Screen A")
Button(onClick = { onNavigateToB() }) {
Text("Go to Screen B")
}
}
}@Composable
fun ScreenB(onNavigateBack: () -> Unit) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Screen B")
Spacer(modifier = Modifier.height(16.dp))
TestSearchBar(onNavigateBack=onNavigateBack)
}
}
@Composable
fun TestSearchBar(onNavigateBack: () -> Unit) {
val textState = remember { mutableStateOf("") }
val focusRequesterOuter = remember { FocusRequester() }
val focusRequesterInner = remember { FocusRequester() }
val focusManager = LocalFocusManager.current
var isTextFieldFocused by rememberSaveable { mutableStateOf(false) }
Row(
modifier = Modifier
.fillMaxWidth()
.shadow(3.dp, RoundedCornerShape(8.dp))
.background(if (isTextFieldFocused) Color.LightGray else Color.Gray)
.focusRequester(focusRequesterOuter)
.focusable()
.onKeyEvent {
if (it.type == KeyEventType.KeyDown) {
when (it.key) {
Key.Back, Key.Escape -> {
println("ESC/Back pressed at OUTER level")
onNavigateBack()
return@onKeyEvent true
}
}
}
false
}
.clickable { focusRequesterInner.requestFocus() }, // Clicking outer restores focus
verticalAlignment = Alignment.CenterVertically
) {
Icon(Icons.Default.Search, contentDescription = "Search", modifier = Modifier.padding(8.dp))
BasicTextField(
value = textState.value,
onValueChange = { textState.value = it },
modifier = Modifier
.weight(1f)
.focusRequester(focusRequesterInner)
.onFocusChanged { isTextFieldFocused = it.isFocused }
.onKeyEvent {
if (it.type == KeyEventType.KeyDown) {
when (it.key) {
Key.Back, Key.Escape -> {
println("ESC/Back pressed at INNER level")
focusRequesterOuter.requestFocus() // Shift focus back to outer container
return@onKeyEvent true // Prevent system back behavior
}
}
}
false
},
singleLine = true,
textStyle = TextStyle(color = Color.Black),
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
keyboardActions = KeyboardActions(
onSearch = { focusManager.clearFocus() }
)
)
}
LaunchedEffect(Unit) {
focusRequesterInner.requestFocus() // Ensure field is focused at start
}
}
Thomas
02/19/2025, 12:11 AMcompose-multiplatform = 1.8.0-alpha02
, androidx-lifecycle = 2.9.0-alpha02
, and androidx-navigation = 2.8.0-alpha12
. However, with compose-multiplatform = 1.8.0-alpha03
, androidx-lifecycle = 2.9.0-alpha03
, and androidx-navigation = 2.8.0-alpha13
, the ESC
event unexpectedly triggers a navigation back from the inner level.Alexander Maryanovsky
02/19/2025, 6:44 AMKonstantin Tskhovrebov
02/19/2025, 2:06 PM