Criar apps adaptáveis com o Jetpack Compose

1. Introdução

Neste codelab, você vai aprender a criar apps adaptáveis para smartphones, tablets e dispositivos dobráveis e como eles melhoram a acessibilidade com o Jetpack Compose. Você também vai aprender as práticas recomendadas para usar os componentes e temas do Material 3.

Antes de começar, é importante entender o que significa adaptabilidade.

Adaptabilidade

A interface do app precisa ser responsiva para considerar diferentes tamanhos de janela, orientações e formatos. Um layout adaptável muda com base no espaço de tela disponível. Essas mudanças vão desde ajustes simples para preencher a área e escolher os respectivos estilos de navegação até mudanças completas de layout para usar mais espaço.

Para saber mais, acesse Design adaptável.

Neste codelab, você vai aprender a usar e pensar em adaptabilidade ao utilizar o Jetpack Compose. Você vai criar um app chamado "Reply", que mostra como implementar a adaptabilidade para todos os tipos de tela e como a adaptabilidade e a acessibilidade funcionam juntas para dar aos usuários uma experiência ideal.

O que você vai aprender

  • Como projetar seu app para todos os tamanhos de janela com o Jetpack Compose.
  • Como destinar seu app a diferentes dispositivos dobráveis.
  • Como usar diferentes tipos de navegação para melhor adaptabilidade e acessibilidade.
  • Como usar os componentes do Material 3 para oferecer a melhor experiência em todos os tamanhos de janela.

O que é necessário

  • A versão estável mais recente do Android Studio.
  • Um dispositivo virtual redimensionável do Android 13.
  • Conhecimento sobre Kotlin.
  • Noções básicas do Compose, como a anotação @Composable.
  • Conhecimento básico sobre os layouts do Compose, como Row e Column.
  • Conhecimento básico sobre modificadores (por exemplo, Modifier.padding()).

Neste codelab, você vai usar o emulador redimensionável, que permite alternar entre diferentes tipos de dispositivos e tamanhos de janela.

Emulador redimensionável com opções de smartphone, dispositivo desdobrado, tablet e computador.

Se você não conhece o Compose, faça o codelab de Noções básicas do Jetpack Compose antes deste.

O que você vai criar

  • Um app interativo de cliente de e-mail chamado Reply que usa as práticas recomendadas para designs adaptáveis, diferentes navegações no Material e uso ideal do espaço na tela.

Demonstração do suporte a vários dispositivos que você conseguirá implantar neste codelab.

2. Começar a configuração

Para acessar o código deste codelab, clone o repositório do GitHub na linha de comando:

git clone https://github.com/android/codelab-android-compose.git
cd codelab-android-compose/AdaptiveUiCodelab

Se preferir, baixe o repositório como um arquivo ZIP:

Recomendamos que você comece com o código na ramificação main e siga as etapas do codelab no seu ritmo.

Abrir o projeto no Android Studio

  1. Na janela Welcome to Android Studio, selecione c01826594f360d94.pngOpen an Existing Project.
  2. Selecione a pasta <Download Location>/AdaptiveUiCodelab. Dica: você precisa selecionar o diretório AdaptiveUiCodelab que contém build.gradle.
  3. Depois que o Android Studio tiver importado o projeto, teste se você consegue executar a ramificação main.

Conhecer o código inicial

O código da ramificação main contém o pacote ui. Você vai trabalhar com os seguintes arquivos nesse pacote:

  • MainActivity.kt: atividade do ponto de entrada em que você inicia o app.
  • ReplyApp.kt: contém os elementos combináveis da interface da tela principal.
  • ReplyHomeViewModel.kt: fornece os dados e o estado da interface do conteúdo do app.
  • ReplyListContent.kt: contém elementos combináveis para fornecer listas e telas de detalhes.

Se você executar esse app em um emulador redimensionável e testar tipos de dispositivos diferentes, como um smartphone ou tablet, a interface vai se expandir para o espaço fornecido em vez de aproveitar o espaço da tela ou fornecer ergonomia de acessibilidade.

Tela inicial no smartphone.

Visualização esticada inicial no tablet.

Você vai atualizar para aproveitar o espaço da tela, aumentar a usabilidade e melhorar a experiência geral do usuário.

3. Tornar apps adaptáveis

Esta seção apresenta o que significa tornar os apps adaptáveis e quais componentes o Material 3 oferece para facilitar isso. Também vamos abordar os tipos de tela e estados para os quais você quer destinar o app, incluindo smartphones, tablets, tablets grandes e dispositivos dobráveis.

Você vai começar aprendendo sobre os fundamentos de tamanhos de janela, posições de dobra e diferentes tipos de opções de navegação. Em seguida, você pode usar essas APIs no seu app para que ele fique mais adaptável.

Tamanhos de janela

Existem dispositivos Android de todos os formatos e tamanhos, sejam smartphones, dobráveis, tablets ou dispositivos ChromeOS. Para oferecer suporte ao maior número possível de tamanhos de janela, a interface precisa ser responsiva e adaptável. Para ajudar você a encontrar o limite certo em que a interface do app precisa mudar, definimos valores de ponto de interrupção que ajudam a classificar dispositivos em classes de tamanho predefinidas (compactas, médias e expandidas), chamadas de classes de tamanho de janelas. Esses são um conjunto de pontos de interrupção específicos da janela de visualização que ajudam você a projetar, desenvolver e testar layouts responsivos e adaptáveis para seu app.

As categorias foram escolhidas especificamente para equilibrar a simplicidade do layout com a flexibilidade de otimizar o app para casos únicos. A classe de tamanho de janela sempre é determinada pelo espaço na tela disponível para o app, que pode não ocupar a tela física inteira para multitarefas ou outras segmentações.

WindowWidthSizeClass para largura compacta, média e expandida.

WindowHeightSizeClass para altura compacta, média e expandida.

A largura e a altura são classificadas separadamente. A todo momento, o app tem duas classes de tamanho de janela: uma para largura e outra para altura. A largura disponível geralmente é mais importante que a altura disponível devido à onipresença da rolagem vertical. Portanto, você também vai usar classes de tamanho de largura nesse caso.

Estados de dobra

Os dispositivos dobráveis apresentam ainda mais situações em que o app pode se adaptar devido aos tamanhos variados e à presença de dobradiças. As dobradiças podem ocultar parte da tela, tornando essa área inadequada para mostrar conteúdo. Elas também podem ser separadas, o que significa que há duas telas físicas separadas quando o dispositivo está aberto.

Posições dobráveis, aberta e meio aberta

Além disso, o usuário pode estar olhando para o display interno enquanto a dobradiça está parcialmente aberta, resultando em diferentes posturas físicas com base na orientação da dobra: postura de mesa (dobra horizontal, mostrada à direita na imagem acima) e postura de livro (dobra vertical).

Saiba mais sobre articulações e posições da dobra.

Tudo isso precisa ser considerado ao implementar layouts adaptáveis compatíveis com dispositivos dobráveis.

Receber informações adaptativas

A biblioteca Material3 adaptive oferece acesso conveniente a informações sobre a janela em que o app está sendo executado.

  1. Adicione entradas para esse artefato e a versão dele ao arquivo de catálogo de versões:

gradle/libs.versions.toml

[versions]
material3Adaptive = "1.0.0"

[libraries]
androidx-material3-adaptive = { module = "androidx.compose.material3.adaptive:adaptive", version.ref = "material3Adaptive" }
  1. No arquivo de build do módulo do app, adicione a nova dependência de biblioteca e faça uma sincronização do Gradle:

app/build.gradle.kts

dependencies {

    implementation(libs.androidx.material3.adaptive)
}

Agora, em qualquer escopo combinável, você pode usar currentWindowAdaptiveInfo() para receber um objeto WindowAdaptiveInfo com informações como a classe de tamanho de janela atual e se o dispositivo está em uma postura dobrável, como a de mesa.

Teste agora em MainActivity.

  1. Em onCreate() dentro do bloco ReplyTheme, receba as informações adaptáveis da janela e mostre as classes de tamanho em um elemento combinável Text. Você pode adicionar isso depois do elemento ReplyApp():

MainActivity.kt

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    setContent {
        ReplyTheme {
            val uiState by viewModel.uiState.collectAsStateWithLifecycle()
            ReplyApp(
                replyHomeUIState = uiState,
                onEmailClick = viewModel::setSelectedEmail
            )

            val adaptiveInfo = currentWindowAdaptiveInfo()
            val sizeClassText =
                "${adaptiveInfo.windowSizeClass.windowWidthSizeClass}\n" +
                "${adaptiveInfo.windowSizeClass.windowHeightSizeClass}"
            Text(
                text = sizeClassText,
                color = Color.Magenta,
                modifier = Modifier.padding(
                    WindowInsets.safeDrawing.asPaddingValues()
                )
            )
        }
    }
}

Executar o app agora vai mostrar as classes de tamanho de janela impressas sobre o conteúdo do app. Confira o que mais é fornecido nas informações adaptáveis da janela. Depois, remova este Text, já que ele cobre o conteúdo do app e não será necessário para as próximas etapas.

4. Navegação dinâmica

Agora, você vai adaptar a navegação do app conforme o estado e o tamanho do dispositivo mudam para facilitar o uso.

Quando os usuários seguram um smartphone, os dedos geralmente ficam na parte de baixo da tela. Quando os usuários seguram um dispositivo dobrável aberto ou um tablet, os dedos geralmente ficam perto das laterais. Os usuários precisam conseguir navegar ou iniciar uma interação com um app sem precisar de posições extremas ou mudar a posição das mãos.

Ao projetar o app e decidir onde colocar elementos interativos da interface no layout, considere as implicações ergonômicas de diferentes regiões da tela.

  • Quais áreas são confortáveis de alcançar ao segurar o dispositivo?
  • Quais áreas podem ser alcançadas apenas esticando os dedos, o que pode ser inconveniente?
  • Quais áreas são difíceis de alcançar ou estão longe de onde o usuário segura o dispositivo?

A navegação é a primeira coisa com que os usuários interagem e contém ações de alta importância relacionadas a jornadas críticas do usuário. Por isso, ela precisa ser colocada em áreas de fácil acesso. A biblioteca adaptável do Material Design oferece vários componentes que ajudam a implementar a navegação, dependendo da classe de tamanho da janela do dispositivo.

Navegação inferior

A navegação na parte de baixo é perfeita para tamanhos compactos, porque seguramos naturalmente o dispositivo de maneira que o polegar alcance com facilidade todos os pontos de contato da navegação na parte de baixo. Use essa opção sempre que tiver um dispositivo compacto ou um dobrável em um estado compacto dobrado.

Barra de navegação na parte de baixo com itens.

Para uma janela de largura média, a coluna de navegação é ideal para acessibilidade, já que o polegar fica naturalmente na lateral do dispositivo. Você também pode combinar uma coluna de navegação com uma gaveta de navegação para mostrar mais informações.

Coluna de navegação com itens.

A gaveta de navegação oferece uma maneira fácil de mostrar informações detalhadas das guias de navegação e pode ser acessada com facilidade ao usar tablets ou dispositivos maiores. Há dois tipos de gaveta de navegação disponíveis: uma modal e uma permanente.

Gaveta de navegação modal

Você também pode usar uma gaveta de navegação modal para tablets e smartphones de tamanho compacto a médio, porque ela pode ficar oculta ou ser aberta como uma sobreposição no conteúdo. Às vezes, isso pode ser combinado com uma barra de navegação.

Gaveta de navegação modal com itens.

Gaveta de navegação permanente

Você pode usar uma gaveta de navegação permanente para navegação fixa em tablets grandes, Chromebooks e computadores.

Gaveta de navegação permanente com itens.

Implementar a navegação dinâmica

Agora, você vai alternar entre diferentes tipos de navegação conforme o estado e o tamanho do dispositivo mudam.

No momento, o app sempre mostra um NavigationBar abaixo do conteúdo da tela, independentemente do estado do dispositivo. Em vez disso, use o componente NavigationSuiteScaffold do Material para alternar automaticamente entre os diferentes componentes de navegação com base em informações como a classe de tamanho da janela atual.

  1. Adicione a dependência do Gradle para receber esse componente atualizando o catálogo de versões e o script de build do app. Depois, faça uma sincronização do Gradle:

gradle/libs.versions.toml

[versions]
material3AdaptiveNavSuite = "1.3.0"

[libraries]
androidx-material3-adaptive-navigation-suite = { module = "androidx.compose.material3:material3-adaptive-navigation-suite", version.ref = "material3AdaptiveNavSuite" }

app/build.gradle.kts

dependencies {

    implementation(libs.androidx.material3.adaptive.navigation.suite)
}
  1. Encontre a função combinável ReplyNavigationWrapper() em ReplyApp.kt e substitua o Column e o conteúdo dele por um NavigationSuiteScaffold:

ReplyApp.kt

@Composable
private fun ReplyNavigationWrapperUI(
    content: @Composable () -> Unit = {}
) {
    var selectedDestination: ReplyDestination by remember {
        mutableStateOf(ReplyDestination.Inbox)
    }

    NavigationSuiteScaffold(
        navigationSuiteItems = {
            ReplyDestination.entries.forEach {
                item(
                    selected = it == selectedDestination,
                    onClick = { /*TODO update selection*/ },
                    icon = {
                        Icon(
                            imageVector = it.icon,
                            contentDescription = stringResource(it.labelRes)
                        )
                    },
                    label = {
                        Text(text = stringResource(it.labelRes))
                    },
                )
            }
        }
    ) {
        content()
    }
}

O argumento navigationSuiteItems é um bloco que permite adicionar itens usando a função item(), semelhante à adição de itens em um LazyColumn. Dentro da lambda final, esse código chama o content() transmitido como argumento para ReplyNavigationWrapperUI().

Execute o app no emulador e tente mudar os tamanhos entre smartphone, dobrável e tablet. Você vai notar que a barra de navegação muda para uma coluna de navegação e volta.

Em janelas muito largas, como em um tablet no modo paisagem, talvez seja melhor mostrar a gaveta de navegação permanente. O NavigationSuiteScaffold oferece suporte à exibição de um painel permanente, mas ele não aparece em nenhum dos valores WindowWidthSizeClass atuais. No entanto, é possível fazer isso com uma pequena mudança.

  1. Adicione o seguinte código antes da chamada para NavigationSuiteScaffold:

ReplyApp.kt

@Composable
private fun ReplyNavigationWrapperUI(
    content: @Composable () -> Unit = {}
) {
    var selectedDestination: ReplyDestination by remember {
        mutableStateOf(ReplyDestination.Inbox)
    }

    val windowSize = with(LocalDensity.current) {
        currentWindowSize().toSize().toDpSize()
    }
    val layoutType = if (windowSize.width >= 1200.dp) {
        NavigationSuiteType.NavigationDrawer
    } else {
        NavigationSuiteScaffoldDefaults.calculateFromAdaptiveInfo(
            currentWindowAdaptiveInfo()
        )
    }

    NavigationSuiteScaffold(
        layoutType = layoutType,
        ...
    ) {
        content()
    }
}

Esse código primeiro recebe o tamanho da janela e o converte em unidades DP usando currentWindowSize() e LocalDensity.current. Depois, ele compara a largura da janela para decidir o tipo de layout da interface de navegação. Se a largura da janela for pelo menos 1200.dp, ela usará NavigationSuiteType.NavigationDrawer. Caso contrário, ele volta ao cálculo padrão.

Quando você executar o app novamente no emulador redimensionável e testar tipos diferentes, observe que sempre que a configuração da tela mudar ou quando você desdobrar um dispositivo dobrável, a navegação mudará para o tipo adequado para esse tamanho.

Mudanças de adaptação para diferentes tamanhos de dispositivos.

Parabéns! Você aprendeu sobre os diferentes tipos de navegação para oferecer suporte a diferentes tipos de tamanhos e estados de janela.

Na próxima seção, você vai aprender a aproveitar qualquer área de tela restante em vez de estender o mesmo item da lista de uma borda à outra.

5. Uso do espaço na tela

Não importa se você está executando o app em um tablet pequeno, um dispositivo desdobrado ou um tablet grande, a tela é esticada para preencher o espaço restante. Você pode aproveitar esse espaço na tela para mostrar mais informações. No caso deste app, por exemplo, mostrando e-mails e conversas aos usuários na mesma página.

O Material 3 define três layouts canônicos, cada um com configurações para classes de tamanho de janela compacta, média e expandida. O layout canônico Detalhes da lista é perfeito para esse caso de uso e está disponível no Compose como ListDetailPaneScaffold.

  1. Para receber esse componente, adicione as seguintes dependências e faça uma sincronização do Gradle:

gradle/libs.versions.toml

[libraries]
androidx-material3-adaptive-layout = { module = "androidx.compose.material3.adaptive:adaptive-layout", version.ref = "material3Adaptive" }
androidx-material3-adaptive-navigation = { module = "androidx.compose.material3.adaptive:adaptive-navigation", version.ref = "material3Adaptive" }

app/build.gradle.kts

dependencies {

    implementation(libs.androidx.material3.adaptive.layout)
    implementation(libs.androidx.material3.adaptive.navigation)
}
  1. Encontre a função combinável ReplyAppContent() em ReplyApp.kt, que atualmente mostra apenas o painel de lista chamando ReplyListPane(). Substitua essa implementação por ListDetailPaneScaffold inserindo o seguinte código. Como essa é uma API experimental, também vamos adicionar a anotação @OptIn à função ReplyAppContent():

ReplyApp.kt

@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
fun ReplyAppContent(
    replyHomeUIState: ReplyHomeUIState,
    onEmailClick: (Email) -> Unit,
) {
    val navigator = rememberListDetailPaneScaffoldNavigator<Long>()

    ListDetailPaneScaffold(
        directive = navigator.scaffoldDirective,
        value = navigator.scaffoldValue,
        listPane = {
            ReplyListPane(replyHomeUIState, onEmailClick)
        },
        detailPane = {
            ReplyDetailPane(replyHomeUIState.emails.first())
        }
    )
}

Primeiro, esse código cria um navegador usando rememberListDetailPaneNavigator(). O navegador oferece algum controle sobre qual painel é exibido e qual conteúdo deve ser representado nele, o que será demonstrado mais tarde.

O ListDetailPaneScaffold vai mostrar dois painéis quando a classe de tamanho de largura da janela for expandida. Caso contrário, ele vai mostrar um painel ou outro com base nos valores fornecidos para dois parâmetros: a diretiva e o valor do scaffold. Para ter o comportamento padrão, esse código usa a diretiva e o valor de scaffold fornecidos pelo navegador.

Os parâmetros obrigatórios restantes são lambdas combináveis para os painéis. ReplyListPane() e ReplyDetailPane() (encontrados em ReplyListContent.kt) são usados para preencher as funções dos painéis de lista e detalhes, respectivamente. ReplyDetailPane() espera um argumento de e-mail. Por enquanto, esse código usa o primeiro e-mail da lista em ReplyHomeUIState.

Execute o app e mude a visualização do emulador para dobrável ou tablet. Talvez seja necessário mudar a orientação para ver o layout de dois painéis. Já está muito melhor do que antes!

Agora vamos abordar alguns dos comportamentos desejados dessa tela. Quando o usuário toca em um e-mail no painel de lista, ele aparece no painel de detalhes com todas as respostas. No momento, o app não acompanha qual e-mail foi selecionado, e tocar em um item não faz nada. O melhor lugar para manter essas informações é com o restante do estado da interface em ReplyHomeUIState.

  1. Abra ReplyHomeViewModel.kt e encontre a classe de dados ReplyHomeUIState. Adicione uma propriedade para o e-mail selecionado, com um valor padrão de null:

ReplyHomeViewModel.kt

data class ReplyHomeUIState(
    val emails : List<Email> = emptyList(),
    val selectedEmail: Email? = null,
    val loading: Boolean = false,
    val error: String? = null
)
  1. No mesmo arquivo, ReplyHomeViewModel tem uma função setSelectedEmail() que é chamada quando o usuário toca em um item da lista. Modifique essa função para copiar o estado da interface e gravar o e-mail selecionado:

ReplyHomeViewModel.kt

fun setSelectedEmail(email: Email) {
    _uiState.update {
        it.copy(selectedEmail = email)
    }
}

É importante considerar o que acontece antes de o usuário tocar em um item e quando o e-mail selecionado é null. O que deve ser mostrado no painel de detalhes? Há várias maneiras de lidar com esse caso, como mostrar o primeiro item da lista por padrão.

  1. No mesmo arquivo, modifique a função observeEmails(). Quando a lista de e-mails for carregada, se o estado anterior da interface não tiver um e-mail selecionado, defina-o como o primeiro item:

ReplyHomeViewModel.kt

private fun observeEmails() {
    viewModelScope.launch {
        emailsRepository.getAllEmails()
            .catch { ex ->
                _uiState.value = ReplyHomeUIState(error = ex.message)
            }
            .collect { emails ->
                val currentSelection = _uiState.value.selectedEmail
                _uiState.value = ReplyHomeUIState(
                    emails = emails,
                    selectedEmail = currentSelection ?: emails.first()
                )
            }
    }
}
  1. Volte para ReplyApp.kt e use o e-mail selecionado, se disponível, para preencher o conteúdo do painel de detalhes:

ReplyApp.kt

ListDetailPaneScaffold(
    // ...
    detailPane = {
        if (replyHomeUIState.selectedEmail != null) {
            ReplyDetailPane(replyHomeUIState.selectedEmail)
        }
    }
)

Execute o app novamente, mude o emulador para o tamanho de um tablet e veja que tocar em um item da lista atualiza o conteúdo do painel de detalhes.

Isso funciona muito bem quando os dois painéis estão visíveis, mas quando a janela só tem espaço para mostrar um painel, parece que nada acontece quando você toca em um item. Tente mudar a visualização do emulador para um smartphone ou um dispositivo dobrável no modo retrato. Observe que apenas o painel de lista fica visível mesmo depois de tocar em um item. Isso acontece porque, mesmo que o e-mail selecionado seja atualizado, o ListDetailPaneScaffold mantém o foco no painel de lista nessas configurações.

  1. Para corrigir isso, insira o seguinte código como a lambda transmitida para ReplyListPane:

ReplyApp.kt

ListDetailPaneScaffold(
    // ...
    listPane = {
        ReplyListPane(
            replyHomeUIState = replyHomeUIState,
            onEmailClick = { email ->
                onEmailClick(email)
                navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, email.id)
            }
        )
    },
    // ...
)

Essa lambda usa o navegador criado anteriormente para adicionar um comportamento extra quando um item é clicado. Ele vai chamar a lambda original transmitida para essa função e também navigator.navigateTo(), especificando qual painel deve ser mostrado. Cada painel no scaffold tem uma função associada a ele, e para o painel de detalhes, é ListDetailPaneScaffoldRole.Detail. Em janelas menores, isso vai dar a impressão de que o app avançou.

O app também precisa processar o que acontece quando o usuário pressiona o botão "Voltar" no painel de detalhes. Esse comportamento será diferente dependendo se há um ou dois painéis visíveis.

  1. Adicione o código abaixo para oferecer suporte à navegação de retorno.

ReplyApp.kt

@OptIn(ExperimentalMaterial3AdaptiveApi::class)
@Composable
fun ReplyAppContent(
    replyHomeUIState: ReplyHomeUIState,
    onEmailClick: (Email) -> Unit,
) {
    val navigator = rememberListDetailPaneScaffoldNavigator<Long>()

    BackHandler(navigator.canNavigateBack()) {
        navigator.navigateBack()
    }

    ListDetailPaneScaffold(
        directive = navigator.scaffoldDirective,
        value = navigator.scaffoldValue,
        listPane = {
            AnimatedPane {
                ReplyListPane(
                    replyHomeUIState = replyHomeUIState,
                    onEmailClick = { email ->
                        onEmailClick(email)
                        navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, email.id)
                    }
                )
            }
        },
        detailPane = {
            AnimatedPane {
                if (replyHomeUIState.selectedEmail != null) {
                    ReplyDetailPane(replyHomeUIState.selectedEmail)
                }
            }
        }
    )
}

O navegador conhece o estado completo do ListDetailPaneScaffold, se a navegação de retorno é possível e o que fazer em todos esses cenários. Esse código cria um BackHandler que é ativado sempre que o navegador pode voltar, e dentro do lambda chama navigateBack(). Além disso, para tornar a transição entre painéis muito mais suave, cada painel é envolvido em um elemento combinável AnimatedPane().

Execute o app novamente em um emulador redimensionável para todos os tipos diferentes de dispositivo e observe que sempre que a configuração da tela muda ou quando abrimos um dispositivo dobrável, a navegação e o conteúdo da tela mudam dinamicamente em resposta às mudanças de estado. Toque nos e-mails no painel de lista e veja como o layout se comporta em diferentes telas, mostrando os dois painéis lado a lado ou animando entre eles de forma suave.

Mudanças de adaptação para diferentes tamanhos de dispositivos.

Parabéns! Você tornou seu app adaptável a todos os tipos de estados e tamanhos de dispositivo. Você já pode testar esse app em dobráveis, tablets ou outros dispositivos móveis.

6. Parabéns

Parabéns! Você concluiu este codelab e aprendeu a tornar os apps adaptáveis com o Jetpack Compose.

Você aprendeu a verificar o tamanho e o estado de dobra de um dispositivo e atualizar de forma adequada a interface, a navegação e outras funções do app. Além disso, você aprendeu que a adaptabilidade melhora a acessibilidade e a experiência do usuário.

A seguir

Confira os outros codelabs no programa de aprendizagem do Compose.

Apps de exemplo

  • Os exemplos do Compose (link em inglês) são uma coleção de vários apps que incorporam as práticas recomendadas explicadas nos codelabs.

Documentos de referência