Esta página oferece uma visão geral de alto nível das camadas arquitetônicas que compõem o Jetpack Compose e os princípios básicos que fundamentam esse design.
O Jetpack Compose não é um projeto monolítico. Ele é criado com vários módulos que são agrupados para formar uma pilha completa. Ao entender os diferentes módulos que compõem o Jetpack Compose, você pode:
- usar o nível de abstração adequado para criar seu app ou sua biblioteca;
- saber quando é possível "descer" para um nível inferior para ter mais controle ou personalização;
- minimizar suas dependências.
Camadas
As principais camadas do Jetpack Compose são estas:
Figura 1. As principais camadas do Jetpack Compose.
Cada camada é criada com base nos níveis inferiores, combinando funcionalidades para criar componentes de nível superior. Cada uma delas se baseia em APIs públicas das camadas inferiores para verificar os limites do módulo e possibilitar que você substitua qualquer camada, se necessário. Vamos examinar essas camadas de baixo para cima.
- Runtime
- Este módulo fornece os princípios básicos do ambiente de execução do Compose, como
remember
,mutableStateOf
, a anotação@Composable
eSideEffect
. Você pode criar diretamente nessa camada se precisar apenas dos recursos de gerenciamento de árvore do Compose, não da IU dele. - UI
- A camada UI é composta por vários módulos, como
ui-text
,ui-graphics
,ui-tooling
etc. Esses módulos implementam os princípios básicos do kit de ferramentas de IU, comoLayoutNode
,Modifier
, gerenciadores de entrada, layouts personalizados e desenho. Você pode criar nessa camada se precisar apenas dos conceitos fundamentais de um kit de ferramentas de IU. - Foundation
- Este módulo fornece elementos básicos independentes do sistema de design para a IU do Compose,
como
Row
eColumn
,LazyColumn
, o reconhecimento de gestos específicos etc. Você pode desenvolver na camada Foundation para criar seu próprio sistema de design. - Material
- Este módulo oferece uma implementação do sistema Material Design para a IU do Compose, fornecendo um sistema de temas, componentes estilizados, indicações de ondulação e ícones. Crie nessa camada ao usar o Material Design no seu app.
Princípios de design
Um princípio orientador do Jetpack Compose é fornecer recursos pequenos e com foco específico, que podem ser agrupados (ou compostos), em vez de apenas alguns componentes monolíticos. Essa abordagem tem várias vantagens.
Controle
Componentes de nível superior tendem a fazer mais por você, mas limitam a quantidade de controle direto que você tem. Se você precisar de mais controle, é possível "diminuir" para usar um componente de nível inferior.
Por exemplo, para animar a cor de um componente, use a
API
animateColorAsState
:
val color = animateColorAsState(if (condition) Color.Green else Color.Red)
No entanto, se você precisar do componente para sempre começar com cinza, não será possível
fazer isso com essa API. Em vez disso, você pode usar o menu suspenso para usar a API Animatable
de nível inferior:
val color = remember { Animatable(Color.Gray) } LaunchedEffect(condition) { color.animateTo(if (condition) Color.Green else Color.Red) }
A API animateColorAsState
de nível superior é criada com base na API Animatable
de
nível inferior. O uso da API de nível inferior é mais complexo, mas oferece mais
controle. Escolha o nível de abstração que melhor atende às suas necessidades.
Personalização
Criar componentes de nível superior usando elementos menores facilita muito
a personalização de componentes, caso ela seja necessária. Por exemplo, considere a
implementação
do
Button
fornecida pela camada do Material Design:
@Composable fun Button( // … content: @Composable RowScope.() -> Unit ) { Surface(/* … */) { CompositionLocalProvider(/* … */) { // set LocalContentAlpha ProvideTextStyle(MaterialTheme.typography.button) { Row( // … content = content ) } } } }
Um Button
é criado usando quatro componentes:
Um
Surface
do Material Design, que define plano de fundo, forma, gerenciamento de cliques etc.Um
CompositionLocalProvider
, que muda a versão Alfa do conteúdo quando o botão é ativado ou desativado.Um
ProvideTextStyle
, que define o estilo de texto padrão a ser usado.Uma
Row
, que fornece a política de layout padrão para o conteúdo do botão.
Omitimos alguns parâmetros e comentários para deixar a estrutura mais clara, mas
o componente inteiro tem apenas 40 linhas de código, porque ele
apenas combina esses quatro componentes para implementar o botão. Componentes como o Button
são rigorosos quanto aos parâmetros que eles expõem, equilibrando a possibilidade de personalizações
comuns com o excesso de parâmetros, que pode dificultar o uso de
um componente. Os componentes do Material Design, por exemplo, oferecem personalizações especificadas
no sistema do Material Design, facilitando o cumprimento dos princípios desse
sistema.
No entanto, se você quiser fazer uma personalização que vá além dos parâmetros de um componente,
poderá "diminuir" um nível e bifurcar o componente. Por exemplo, o Material
Design especifica que os botões precisam ter um plano de fundo de cor sólida. Se você
precisar de um plano de fundo em gradiente, essa opção não é compatível com os parâmetros do
Button
. Nesse caso, você pode usar a implementação do Button
do Material como
referência e criar seu próprio componente:
@Composable fun GradientButton( // … background: List<Color>, modifier: Modifier = Modifier, content: @Composable RowScope.() -> Unit ) { Row( // … modifier = modifier .clickable(onClick = {}) .background( Brush.horizontalGradient(background) ) ) { CompositionLocalProvider(/* … */) { // set material LocalContentAlpha ProvideTextStyle(MaterialTheme.typography.button) { content() } } } }
A implementação acima continua usando componentes da camada do Material Design,
como os conceitos de
conteúdo Alfa atual
e de estilo de texto atual. No entanto, ela substitui o Surface
do Material Design por uma
Row
e define um estilo para alcançar a aparência desejada.
Se você não quiser usar os conceitos do Material Design, por exemplo, se estiver criando seu próprio sistema de design personalizado, poderá deixar de usar os componentes da camada Foundation:
@Composable fun BespokeButton( // … backgroundColor: Color, modifier: Modifier = Modifier, content: @Composable RowScope.() -> Unit ) { Row( // … modifier = modifier .clickable(onClick = {}) .background(backgroundColor) ) { // No Material components used content() } }
O Jetpack Compose reserva os nomes mais simples para os componentes de nível mais alto. Por exemplo,
androidx.compose.material.Text
é baseado no
androidx.compose.foundation.text.BasicText
.
Isso possibilita fornecer sua implementação com o nome mais
detectável possível, caso você queira substituir níveis mais altos.
Escolher a abstração certa
A filosofia do Compose de criação de componentes reutilizáveis em camadas significa que você nem sempre precisa recorrer aos elementos básicos de nível mais baixo. Muitos componentes de nível superior não só oferecem mais funcionalidades, como geralmente também implementam práticas recomendadas, como compatibilidade com acessibilidade.
Por exemplo, se você quiser adicionar suporte a gestos ao seu componente personalizado, pode
criar isso do zero usando o
Modifier.pointerInput
.
Mas há outros componentes de nível superior criados com base nele que podem
oferecer um ponto de partida melhor, como
o Modifier.draggable
,
o Modifier.scrollable
ou o Modifier.swipeable
.
Como regra, prefira criar com base no componente de nível mais alto que ofereça a funcionalidade necessária para você se beneficiar das práticas recomendadas incluídas.
Saiba mais
Consulte o exemplo do Jetsnack (link em inglês) para saber como criar um sistema de design personalizado.
Recomendados para você
- Observação: o texto do link aparece quando o JavaScript está desativado.
- Kotlin para Jetpack Compose
- Listas e grades
- Efeitos colaterais no Compose