Restablecer el enfoque

Cuando los usuarios navegan por interfaces de usuario complejas con un teclado de hardware o un pad direccional, suelen moverse entre diferentes secciones de la pantalla, por ejemplo, saltar de un riel de navegación lateral a una lista de contenido, interactuar con un elemento y, luego, volver a saltar. La restauración del enfoque permite que los contenedores recuerden el elemento secundario enfocado anteriormente y restablezcan el enfoque en él cuando este vuelva a ingresar al contenedor.

Restablece el enfoque en contenedores desplazables

En Jetpack Compose, los contenedores desplazables, como LazyColumn, LazyRow y LazyVerticalGrid, actúan automáticamente como grupos de enfoque. Para habilitar la restauración del enfoque en una lista o cuadrícula diferida, adjunta el modificador focusRestorer:

LazyColumn(
    modifier = Modifier.focusRestorer()
) {
    items(items) { item ->
        Button(onClick = { /* Handle click */ }) {
            Text(item)
        }
    }
}

Cuando el enfoque sale del LazyColumn, el contenedor registra qué elemento estaba activo. Cuando el usuario vuelve a navegar por la lista, el enfoque se restablece en ese elemento en lugar de reiniciarse en la parte superior.

Restablece el enfoque en Row y Column

A diferencia de los contenedores de diseño diferido, los contenedores de diseño estándar no desplazables, como Row y Column, no establecen un límite de enfoque de forma predeterminada. Como no interceptan los eventos de entrada de enfoque por sí solos, no es suficiente con aplicar solo el modificador focusRestorer.

Para habilitar el restablecimiento del enfoque en Row o Column, combina focusGroup con focusRestorer:

Row(
    modifier = Modifier
        .focusGroup()
        .focusRestorer()
) {
    Button(onClick = { /* Action 1 */ }) {
        Text("First item")
    }
    Button(onClick = { /* Action 2 */ }) {
        Text("Second item")
    }
    Button(onClick = { /* Action 3 */ }) {
        Text("Third item")
    }
}

Cómo especificar un destino de enfoque alternativo

Cuando el enfoque ingresa a un contenedor por primera vez, no existe ningún elemento secundario enfocado anteriormente. Compose sigue el orden de enfoque predeterminado para determinar en qué elemento secundario se debe enfocar.

Puedes personalizar qué elemento secundario recibe el enfoque en la primera entrada pasando un FocusRequester de resguardo a focusRestorer (o una lambda que devuelve un FocusRequester en versiones anteriores de Compose):

val firstItemRequester = remember { FocusRequester() }

LazyColumn(
    modifier = Modifier.focusRestorer(firstItemRequester)
) {
    itemsIndexed(items) { index, item ->
        val itemModifier = if (index == 0) {
            Modifier.focusRequester(firstItemRequester)
        } else {
            Modifier
        }
        Button(
            onClick = { /* Handle click */ },
            modifier = itemModifier
        ) {
            Text(item)
        }
    }
}

Si no se enfocó ningún elemento secundario anteriormente (o si falla la restauración del enfoque), Compose mueve el enfoque al objetivo de resguardo asociado.

En los componentes de navegación, como NavigationRail, NavigationBar, NavigationDrawer o TabRow, los usuarios esperan que el enfoque se centre en el destino seleccionado actualmente cuando ingresan al componente desde el área de contenido.

Puedes dirigir el enfoque al destino activo combinando Modifier.focusGroup() con focusProperties y configurando la propiedad onEnter (o enter en versiones anteriores de Compose):

val (homeRequester, searchRequester, settingsRequester) = remember { FocusRequester.createRefs() }

val selectedRequester = when (currentDestination) {
    "home" -> homeRequester
    "search" -> searchRequester
    "settings" -> settingsRequester
    else -> homeRequester
}

NavigationRail(
    modifier = modifier
        .focusProperties {
            // Redirect focus to the selected item when focus enters the navigation rail
            onEnter = { selectedRequester.requestFocus() }
        }
        .focusGroup()
) {
    NavigationRailItem(
        selected = currentDestination == "home",
        onClick = { onNavigate("home") },
        icon = { Icon(Icons.Default.Home, contentDescription = "Home") },
        label = { Text("Home") },
        modifier = Modifier.focusRequester(homeRequester)
    )
    NavigationRailItem(
        selected = currentDestination == "search",
        onClick = { onNavigate("search") },
        icon = { Icon(Icons.Default.Search, contentDescription = "Search") },
        label = { Text("Search") },
        modifier = Modifier.focusRequester(searchRequester)
    )
    NavigationRailItem(
        selected = currentDestination == "settings",
        onClick = { onNavigate("settings") },
        icon = { Icon(Icons.Default.Settings, contentDescription = "Settings") },
        label = { Text("Settings") },
        modifier = Modifier.focusRequester(settingsRequester)
    )
}

Cómo restablecer el enfoque en las transiciones de pantalla

Cuando un usuario hace clic en un elemento para navegar a una pantalla de detalles y, luego, regresa con el botón o el gesto Atrás, puedes restablecer el enfoque en el elemento en el que hizo clic.

Para conservar y restablecer el enfoque en las transiciones de pantalla, haz lo siguiente:

  1. Asocia el contenedor a un FocusRequester.
  2. Antes de activar el evento de navegación, llama a saveFocusedChild() en el FocusRequester del contenedor.
  3. Cuando el usuario vuelva a la pantalla, solicita el enfoque inicial en el contenedor o llama a restoreFocusedChild().