Os modificadores permitem decorar ou aumentar as funções que podem ser compostas. Com os modificadores, é possível fazer o seguinte:
- Mudar o tamanho, o layout, o comportamento e a aparência do elemento
- Adicionar informações, como rótulos de acessibilidade
- Processar a entrada do usuário
- Adicionar interações de nível superior, como tornar um elemento clicável, rolável, arrastável ou redimensionável
Modificadores são objetos Kotlin padrão. Crie um modificador chamando uma das funções de classe
Modifier
.
@Composable private fun Greeting(name: String) { Column(modifier = Modifier.padding(24.dp)) { Text(text = "Hello,") Text(text = name) } }
É possível encadear essas funções para fazer a composição delas:
@Composable private fun Greeting(name: String) { Column( modifier = Modifier .padding(24.dp) .fillMaxWidth() ) { Text(text = "Hello,") Text(text = name) } }
Observe diferentes funções de modificador sendo usadas juntas no código acima.
padding
cria um espaço ao redor de um elemento.fillMaxWidth
faz a função que pode ser composta preencher a largura máxima atribuída a ela pelo elemento pai.
É uma prática recomendada que todos os elementos combináveis aceitem um parâmetro modifier
e transmitam esse modificador ao primeiro filho que emite a interface.
Isso torna seu
código mais reutilizável e deixa o comportamento dele mais previsível e intuitivo. Para
mais informações, consulte as diretrizes da API Compose: Os elementos aceitam e respeitam um
parâmetro modificador.
A ordem dos modificadores é importante
A ordem das funções modificadoras é importante. Como cada função realiza
mudanças no Modifier
retornado pela função anterior, a sequência
afeta o resultado final. Veja um exemplo disso:
@Composable fun ArtistCard(/*...*/) { val padding = 16.dp Column( Modifier .clickable(onClick = onClick) .padding(padding) .fillMaxWidth() ) { // rest of the implementation } }
No código acima, a área inteira é clicável, incluindo o padding
ao redor dela, porque o modificador
padding
foi aplicado depois do modificador clickable
. Se a ordem dos modificadores for invertida, o espaço adicionado por padding
não
reagirá à entrada do usuário:
@Composable fun ArtistCard(/*...*/) { val padding = 16.dp Column( Modifier .padding(padding) .clickable(onClick = onClick) .fillMaxWidth() ) { // rest of the implementation } }
Modificadores integrados
O Jetpack Compose oferece uma lista de modificadores integrados para ajudar você a decorar ou aumentar uma função que pode ser composta. Veja alguns modificadores comuns que você vai usar para ajustar seus layouts.
padding
e size
Por padrão, os layouts fornecidos no Compose unem os filhos. No entanto,
é possível definir um tamanho usando o modificador size
:
@Composable fun ArtistCard(/*...*/) { Row( modifier = Modifier.size(width = 400.dp, height = 100.dp) ) { Image(/*...*/) Column { /*...*/ } } }
O tamanho especificado pode não ser respeitado caso ele não atenda
às restrições provenientes do pai do layout. Caso você precise que o tamanho
do elemento combinável seja corrigido, independente das restrições de entrada, use o modificador
requiredSize
:
@Composable fun ArtistCard(/*...*/) { Row( modifier = Modifier.size(width = 400.dp, height = 100.dp) ) { Image( /*...*/ modifier = Modifier.requiredSize(150.dp) ) Column { /*...*/ } } }
Nesse exemplo, mesmo com a height
do elemento pai definida como 100.dp
, a altura da
Image
vai ser 150.dp
, já que o modificador requiredSize
tem
precedência.
Caso você queira que um layout filho preencha toda a altura disponibilizada pelo
pai, adicione o modificador fillMaxHeight
. O Compose também oferece fillMaxSize
e
fillMaxWidth
:
@Composable fun ArtistCard(/*...*/) { Row( modifier = Modifier.size(width = 400.dp, height = 100.dp) ) { Image( /*...*/ modifier = Modifier.fillMaxHeight() ) Column { /*...*/ } } }
Para adicionar padding ao redor de um elemento, defina um modificador padding
.
Caso queira adicionar padding acima da linha de base do texto, de modo a estabelecer uma
distância específica do topo do layout até a linha de base, use o modificador paddingFromBaseline
:
@Composable fun ArtistCard(artist: Artist) { Row(/*...*/) { Column { Text( text = artist.name, modifier = Modifier.paddingFromBaseline(top = 50.dp) ) Text(artist.lastSeenOnline) } } }
Offset
Para posicionar um layout em relação à posição original, adicione o modificador
offset
e defina o deslocamento no eixo x e y.
Os deslocamentos podem ser positivos e não positivos. A diferença entre
padding
e offset
é que adicionar um offset
a um elemento combinável não
muda as medidas dele:
@Composable fun ArtistCard(artist: Artist) { Row(/*...*/) { Column { Text(artist.name) Text( text = artist.lastSeenOnline, modifier = Modifier.offset(x = 4.dp) ) } } }
O modificador offset
é aplicado horizontalmente, de acordo com a direção do layout.
Em um contexto de sentido da esquerda para a direita, um offset
positivo desloca o elemento para a
direita. Já em um contexto de sentido da direita para esquerda, o elemento é deslocado para a esquerda.
Caso seja necessário definir um deslocamento sem considerar a direção do layout, analise o
modificador absoluteOffset
,
em que um valor de deslocamento positivo sempre desloca o elemento para a
direita.
O modificador offset
fornece duas sobrecargas: offset
, que usa os
deslocamentos como parâmetros, e offset
, que usa uma lambda.
Para informações mais detalhadas sobre quando usar cada uma delas e como otimizar
o desempenho, leia a seção
Desempenho do Compose: adiar leituras pelo maior tempo possível.
Segurança de escopo no Compose
No Compose, há modificadores que só podem ser usados quando aplicados a filhos de determinados elementos combináveis. Ele aplica essa segurança usando escopos personalizados.
Por exemplo, se você quiser deixar um filho do tamanho do elemento
Box
pai sem afetar o tamanho do Box
, use o
modificador
matchParentSize
. O matchParentSize
está disponível apenas no
BoxScope
.
Portanto, ele só pode ser usado em um filho dentro de um Box
pai.
A segurança de escopo impede que você adicione modificadores que não funcionam em outros escopos e elementos combináveis, além de economizar tempo de tentativa e erro.
Os modificadores com escopo informam o pai sobre algumas informações que ele precisa saber sobre o elemento filho. Eles também costumam ser chamados de modificadores de dados pai. Os componentes internos são diferentes dos modificadores de uso geral, mas do ponto de vista do uso, essas diferenças não importam.
matchParentSize
em Box
Como mencionado acima, se você quiser que um layout filho tenha o mesmo tamanho de uma
Box
mãe sem afetar o tamanho da Box
, use o modificador matchParentSize
.
O matchParentSize
só está disponível em um escopo de Box
, o que significa que
ele se aplica apenas a filhos diretos das funções Box
que podem ser compostas.
No exemplo abaixo, o Spacer
filho tem o tamanho da Box
mãe, que, por sua vez, assume o tamanho dos filhos maiores,
ArtistCard
neste caso.
@Composable fun MatchParentSizeComposable() { Box { Spacer( Modifier .matchParentSize() .background(Color.LightGray) ) ArtistCard() } }
Se fillMaxSize
fosse usado em vez de matchParentSize
, o Spacer
ocuparia
todo o espaço disponibilizado para o pai, fazendo com que o pai fosse
expandido e preenchesse todo o espaço disponível.
weight
em Row
e Column
Conforme abordado na seção anterior sobre preenchimento e
tamanho, por padrão, o tamanho de um elemento combinável é definido pelo
conteúdo que ele agrupa. Você pode definir o tamanho de um elemento combinável como flexível no
pai usando o modificador weight
, disponível apenas em RowScope
, e
ColumnScope
.
Vamos considerar uma Row
contendo duas Box
que podem ser compostas.
A primeira caixa recebe o dobro de weight
da segunda, portanto, duas vezes a
largura. Como a Row
tem 210.dp
de largura, a primeira Box
tem 140.dp
de largura e a
segunda tem 70.dp
:
@Composable fun ArtistCard(/*...*/) { Row( modifier = Modifier.fillMaxWidth() ) { Image( /*...*/ modifier = Modifier.weight(2f) ) Column( modifier = Modifier.weight(1f) ) { /*...*/ } } }
Como extrair e reutilizar modificadores
Vários modificadores podem ser encadeados para decorar
ou aumentar um elemento combinável. Essa cadeia é criada pela interface Modifier
,
que representa uma lista ordenada e imutável de Modifier.Elements
únicos.
Cada Modifier.Element
representa um comportamento individual, como comportamentos de layout, desenho
e gráficos, todos os comportamentos relacionados a gestos, foco e semântica, bem
como eventos de entrada do dispositivo. A ordem deles é importante: os elementos modificadores que são
adicionados primeiro serão aplicados primeiro.
Às vezes, pode ser vantajoso reutilizar as mesmas instâncias de cadeia de modificadores em vários elementos combináveis, extraindo-os em variáveis e os elevando para escopos mais altos. Isso pode melhorar a legibilidade do código ou ajudar a melhorar a performance do app por alguns motivos:
- A nova alocação dos modificadores não acontece quando a recomposição ocorre para elementos de composição.
- As cadeias de modificadores podem ser muito longas e complexas. Portanto, reutilizar a mesma instância de uma cadeia pode aliviar a carga de trabalho de que o ambiente de execução do Compose precisa ao compará-las.
- Essa extração promove a limpeza, a consistência e a manutenção do código em toda a base de código.
Práticas recomendadas para reutilizar modificadores
Crie suas próprias cadeias de Modifier
e as extraia para reutilizá-las em vários
componentes de composição. Não há problema em salvar modificadores, já que
eles são objetos semelhantes a dados:
val reusableModifier = Modifier .fillMaxWidth() .background(Color.Red) .padding(12.dp)
Como extrair e reutilizar modificadores ao observar a mudança frequente de estado
Ao observar estados que mudam com frequência dentro de elementos de composição, como estados
de animação ou scrollState
, pode haver uma quantidade significativa de recomposições
realizadas. Nesse caso, seus modificadores vão ser alocados em cada recomposição
e, possivelmente, para cada frame:
@Composable fun LoadingWheelAnimation() { val animatedState = animateFloatAsState(/*...*/) LoadingWheel( // Creation and allocation of this modifier will happen on every frame of the animation! modifier = Modifier .padding(12.dp) .background(Color.Gray), animatedState = animatedState ) }
Em vez disso, você pode criar, extrair e reutilizar a mesma instância do modificador e transmiti-la para o elemento combinável da seguinte forma:
// Now, the allocation of the modifier happens here: val reusableModifier = Modifier .padding(12.dp) .background(Color.Gray) @Composable fun LoadingWheelAnimation() { val animatedState = animateFloatAsState(/*...*/) LoadingWheel( // No allocation, as we're just reusing the same instance modifier = reusableModifier, animatedState = animatedState ) }
Como extrair e reutilizar modificadores sem escopo
Os modificadores podem não ter escopo ou ter escopo para um elemento combinável específico. No caso de modificadores sem escopo, é possível extraí-los facilmente fora dos elementos combináveis como variáveis simples:
val reusableModifier = Modifier .fillMaxWidth() .background(Color.Red) .padding(12.dp) @Composable fun AuthorField() { HeaderText( // ... modifier = reusableModifier ) SubtitleText( // ... modifier = reusableModifier ) }
Isso pode ser muito útil, principalmente quando combinado com layouts lentos. Na maioria dos casos, é recomendável que todos os itens possivelmente significativos tenham os mesmos modificadores:
val reusableItemModifier = Modifier .padding(bottom = 12.dp) .size(216.dp) .clip(CircleShape) @Composable private fun AuthorList(authors: List<Author>) { LazyColumn { items(authors) { AsyncImage( // ... modifier = reusableItemModifier, ) } } }
Como extrair e reutilizar modificadores com escopo
Ao lidar com modificadores com escopo em determinados elementos de composição, é possível extraí-los para o nível mais alto possível e reutilizá-los quando apropriado:
Column(/*...*/) { val reusableItemModifier = Modifier .padding(bottom = 12.dp) // Align Modifier.Element requires a ColumnScope .align(Alignment.CenterHorizontally) .weight(1f) Text1( modifier = reusableItemModifier, // ... ) Text2( modifier = reusableItemModifier // ... ) // ... }
Você só precisa transmitir os modificadores extraídos e com escopo para os filhos diretos com o mesmo escopo. Consulte a seção Segurança de escopo no Compose para mais informações sobre por que isso é importante:
Column(modifier = Modifier.fillMaxWidth()) { // Weight modifier is scoped to the Column composable val reusableItemModifier = Modifier.weight(1f) // Weight will be properly assigned here since this Text is a direct child of Column Text1( modifier = reusableItemModifier // ... ) Box { Text2( // Weight won't do anything here since the Text composable is not a direct child of Column modifier = reusableItemModifier // ... ) } }
Como encadear ainda mais modificadores extraídos
Para encadear ou anexar ainda mais as cadeias de modificadores extraídas, chame a
função .then()
:
val reusableModifier = Modifier .fillMaxWidth() .background(Color.Red) .padding(12.dp) // Append to your reusableModifier reusableModifier.clickable { /*...*/ } // Append your reusableModifier otherModifier.then(reusableModifier)
Lembre-se de que a ordem dos modificadores é importante.
Saiba mais
Temos uma lista completa de modificadores com os parâmetros e escopos deles.
Para ver mais práticas sobre o uso de modificadores, consulte também o codelab de layouts básicos no Compose ou o repositório Agora no Android.
Para ver mais informações sobre modificadores personalizados e como criá-los, consulte a documentação sobre Layouts personalizados: como usar modificadores de layout.
Recomendados para você
- Observação: o texto do link aparece quando o JavaScript está desativado.
- Conceitos básicos de layout do Compose
- Ações do editor {:#editor-actions}
- Layouts personalizados {:#custom-layouts }