Restaurar o foco

Quando os usuários navegam em interfaces complexas usando um teclado físico ou um botão direcional, eles costumam se mover entre diferentes seções da tela, como pular de uma barra de navegação lateral para uma lista de conteúdo, interagir com um item e voltar. Com a restauração do foco, os contêineres lembram o elemento filho em foco anteriormente e restauram o foco nele quando ele entra novamente no contêiner.

Restaurar o foco em contêineres roláveis

No Jetpack Compose, contêineres roláveis, como LazyColumn, LazyRow e LazyVerticalGrid, atuam automaticamente como grupos de foco. Para ativar a restauração do foco em uma lista ou grade lenta, anexe o modificador focusRestorer:

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

Quando o foco sai do LazyColumn, o contêiner registra qual item estava ativo. Quando o usuário volta para a lista, o foco é restaurado para esse item em vez de reiniciar na parte de cima.

Restaurar o foco em Row e Column

Ao contrário dos contêineres de layout lazy, os contêineres de layout padrão não roláveis, como Row e Column, não estabelecem um limite de foco por padrão. Como eles não interceptam eventos de entrada de foco por conta própria, aplicar apenas o modificador focusRestorer não é suficiente.

Para ativar a restauração do foco em Row ou Column, combine focusGroup com 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")
    }
}

Especificar um destino de foco substituto

Quando o foco entra em um contêiner pela primeira vez, não há um elemento filho focado anteriormente. O Compose segue a ordem de foco padrão para determinar qual filho focar.

Você pode personalizar qual elemento filho recebe o foco na primeira entrada transmitindo um FocusRequester de substituição para focusRestorer (ou uma lambda que retorna um FocusRequester em versões anteriores do 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)
        }
    }
}

Se nenhum filho tiver sido focado antes ou se a restauração do foco falhar, o Compose move o foco para o destino de substituição associado.

Em componentes de navegação, como NavigationRail, NavigationBar, NavigationDrawer ou TabRow, os usuários esperam que o foco seja direcionado ao destino selecionado ao entrar no componente pela área de conteúdo.

É possível direcionar o foco para o destino ativo combinando Modifier.focusGroup() com focusProperties e definindo a propriedade onEnter (ou enter em versões anteriores do 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)
    )
}

Restaurar o foco nas transições de tela

Quando um usuário clica em um item para navegar até uma tela de detalhes e depois volta usando o botão "Voltar" ou gesto, é possível restaurar o foco no item em que ele clicou.

Para preservar e restaurar o foco nas transições de tela:

  1. Associe o contêiner a um FocusRequester.
  2. Antes de acionar o evento de navegação, chame saveFocusedChild() no FocusRequester do contêiner.
  3. Quando o usuário voltar à tela, solicite o foco inicial no container ou chame restoreFocusedChild().