React Native: posicionando elementos com Flex
Neste artigo, vamos descobrir como posicionar elementos na tela do celular com uma ferramenta importante do React Native: o Flex. E vamos conhecer:
- O que é o Flex e para que serve;
- Tamanho de telas e unidades de medida;
- Números fixos e relativos;
- Algumas propriedades do Flex.
Conhecer o Flex do React Native é muito importante para qualquer pessoa desenvolvedora de mobile.
Vamos lá?
Introdução
Um dos passos de desenvolver um aplicativo em mobile é o de cuidar da estilização e disposição de elementos em uma tela de celular. Existem diversos modelos de celulares, cada um com um tamanho de tela diferente. Como organizar, definir tamanhos e espaçamentos sem que elementos ultrapassem os limites do dispositivo? Ou evitar que sobrem espaços enormes nas laterais?
Flex é uma das ferramentas mais importantes para trabalhar com posicionamento de elementos no React Native. E, ao ler a palavra Flex, talvez lhe venha na cabeça algo como Flexbox do CSS.
Fazendo uma pequena comparação entre Flex (do React Native) e Flexbox (do CSS), ambos são semelhantes, mas possuem alguns valores iniciais diferentes e também algumas nomenclaturas diferentes. Mas, caso você já conheça Flexbox, não terá muitos problemas em entender Flex do React Native.
Antes de começar a explicar como Flex funciona, primeiro precisamos entender como funciona nosso "canvas" (podendo também fazer uma analogia com um quadro) e suas dimensões.
Cada celular, um canvas diferente
Cada dispositivo tem um tamanho de tela diferente e cada tela tem uma resolução diferente. Até aí, tudo bem. O problema começa quando comparamos as unidades de medida. Uma tela é medida em polegadas e a resolução de uma tela é medida em pixels.
Uma polegada não necessariamente tem um número fixo de pixels. Por exemplo, existem celulares de 4 polegadas que tem uma tela de resolução 720p (720 pixels), e existem celulares de 5 polegadas que tem uma tela de resolução 480p (480 pixels).
Essas informações são importantes, porque quando escrevemos "tamanho" em código, nós utilizamos a unidade de medida pixel. Por conta disso, cada celular vai mostrar elementos com tamanhos (físicos) diferentes mesmo que em código eles tenham o mesmo tamanho.
Uma solução para resolver essa inconsistência é utilizar unidades de medida relativas como porcentagem. Em React Native, nós utilizamos apenas duas unidades de medida: pixels ou px
e porcentagem ou %
.
É muito importante entender que não existe uma maneira certa ou uma unidade perfeita. Utilizamos px
ou %
dependendo da necessidade.
Porém, mesmo com unidades relativas, ainda encontramos algumas limitações. Vou usar o código abaixo como exemplo:
// App.js
import { StyleSheet, StatusBar, SafeAreaView, Text, View } from 'react-native';
export default function App() {
return (
<SafeAreaView>
<StatusBar/>
<View style={styles.quadradoA}/>
<View style={styles.quadradoB}/>
<View style={styles.quadradoC}/>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
quadradoA: {
backgroundColor: "green",
height: 100
},
quadradoB: {
backgroundColor: "blue",
height: 100
},
quadradoC: {
backgroundColor: "orange",
height: 100
}
});
Neste exemplo, foi definida uma altura de 100px
para cada retângulo. Como podemos fazer para que cada retângulo ocupe 1/3 da tela do dispositivo?
Com unidades fixas como px
fica muito difícil de definir. Cada dispositivo tem um tamanho de tela diferente, portanto, a altura pode variar.
Vamos ver como ficaria com a unidade relativa %
. Para calcular a porcentagem de altura que cada retângulo deve ter, basta resolver a conta 1/3. O resultado é 33,33333333333333333...%. Bom... acho que já deu para perceber o problema aqui também: O número 33,333... não é um valor preciso. O ideal seria utilizar frações, mas o React Native não consegue trabalhar com esse tipo de valor. Então precisamos converter para %
, o que acabou gerando a dízima periódica.
Ou seja, mesmo com porcentagens é complicado definir um tamanho exato para dimensionar e posicionar os elementos em tela. Como fazemos então?
O Flex veio para resolver
O Flex é uma ferramenta que veio para ajudar na disposição de elementos na tela do dispositivo. Mas a pergunta é: onde vamos aplicar a propriedade flex
?
Primeiro, precisamos entender o que essa propriedade faz. Quando aplicamos flex
em um componente, ele se transforma em um flex container. Todos os componentes que são descendentes diretos do flex container podem ser remanejados com outras propriedades de Flex, por exemplo, alinhamento vertical, alinhamento horizontal, espaçamento, entre outros.
Por padrão, todos os componentes já são um flex container, sem precisar definir nada. Mas, então, o que a propriedade flex
faz? A propriedade flex
diz qual o tamanho proporcional que o componente vai ocupar. Por padrão, o valor é 0. Ou seja, o componente só vai ocupar o espaço necessário para mostrar o conteúdo.
Vamos ver algumas propriedades de Flex e espaçar os retângulos pela página. Para isso, vamos utilizar a propriedade justifyContent
. Esta propriedade pega todo o espaço restante dentro do flex container e divide entre os elementos. O valor que vamos utilizar é space-between
.
import { StyleSheet, StatusBar, SafeAreaView, Text, View } from 'react-native';
export default function App() {
return (
<SafeAreaView style={styles.container}>
<StatusBar/>
<View style={styles.quadradoA}/>
<View style={styles.quadradoB}/>
<View style={styles.quadradoC}/>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
justifyContent: "space-between"
},
quadradoA: {
backgroundColor: "green",
height: 100
},
quadradoB: {
backgroundColor: "blue",
height: 100
},
quadradoC: {
backgroundColor: "orange",
height: 100
}
});
Rodando o código acima, podemos perceber que não houve mudanças. O que será que aconteceu?
Primeiro vamos lembrar que, por padrão, o valor de flex
é 0. Assim, o componente SafeAreaView
só está ocupando o espaço necessário para mostrar os retângulos. Como a propriedade justifyContent
distribui o espaço vazio entre os componentes e não existe espaço vazio dentro do flex container, os retângulos vão continuar grudados.
Para inserir um espaço entre os blocos coloridos,precisamos trocar o valor padrão de 0 para 1 na propriedade flex
. Veja o código abaixo:
import { StyleSheet, StatusBar, SafeAreaView, Text, View } from 'react-native';
export default function App() {
return (
<SafeAreaView style={styles.container}>
<StatusBar/>
<View style={styles.quadradoA}/>
<View style={styles.quadradoB}/>
<View style={styles.quadradoC}/>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "space-between"
},
quadradoA: {
backgroundColor: "green",
height: 100
},
quadradoB: {
backgroundColor: "blue",
height: 100
},
quadradoC: {
backgroundColor: "orange",
height: 100
}
});
Agora, os retângulos estão com um espaço entre si.
O que mais podemos fazer? Vamos alterar o fluxo dos componentes, da vertical (coluna) para a horizontal (linha). Para isso vamos usar a propriedade flexDirection
.
import { StyleSheet, StatusBar, SafeAreaView, Text, View } from 'react-native';
export default function App() {
return (
<SafeAreaView style={styles.container}>
<StatusBar/>
<View style={styles.quadradoA}/>
<View style={styles.quadradoB}/>
<View style={styles.quadradoC}/>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "space-between",
flexDirection: "row"
},
quadradoA: {
backgroundColor: "green",
height: 100
},
quadradoB: {
backgroundColor: "blue",
height: 100
},
quadradoC: {
backgroundColor: "orange",
height: 100
}
});
O resultado deve ser de uma tela vazia. Mas o que será que aconteceu? Por padrão, cada componente tem os valores de altura (height) e largura (width) como auto
.
Para altura, auto
significa altura mínima necessária para mostrar o componente.
Para largura, auto
significa ocupar o máximo de espaço disponível. No entanto, quando mudamos algumas propriedades de Flex, por exemplo, a direção, auto
significa largura mínima necessária para mostrar o componente.
Como os três componentes estão vazios, a sua largura vai ser de 0px
: é por isso que a tela pareceu vazia.
Logo, vamos definir uma largura para esses retângulos. Temos duas opções para cada retângulo: utilizar a propriedade width
ou a propriedade flex
.
Para este exemplo, vou utilizar as duas maneiras com valores diferentes:
import { StyleSheet, StatusBar, SafeAreaView, Text, View } from 'react-native';
export default function App() {
return (
<SafeAreaView style={styles.container}>
<StatusBar/>
<View style={styles.quadradoA}/>
<View style={styles.quadradoB}/>
<View style={styles.quadradoC}/>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "space-between",
flexDirection: "row"
},
quadradoA: {
backgroundColor: "green",
height: 100,
width: 150,
},
quadradoB: {
backgroundColor: "blue",
height: 100,
flex: 1,
},
quadradoC: {
backgroundColor: "orange",
height: 100,
flex: 2,
}
});
Com o código acima, obtemos o resultado mostrado na imagem abaixo:
O primeiro retângulo verde tem exatos 150px
de largura, o retângulo azul ocupa 1/3 do espaço restante (largura da tela em pixels - 150px do primeiro retângulo) e o retângulo laranja ocupa duas vezes o espaço do retângulo azul.
Conclusão
Flex é uma ferramenta bem poderosa para posicionamento de elementos na tela. Existem muitas outras propriedades e valores de que não falei aqui no artigo. É interessante usar estes conceitos vistos aqui e testar o que os outros valores e propriedades fazem.
O link para a documentação oficial (em inglês) do Flex no React Native com exemplos interativos: Layout with Flexbox.
Gostou deste artigo? Tem sugestões? Marque a gente nas redes sociais!
Quer conhecer mais sobre React Native? Confira alguns de nossos cursos: