Layouts de App Comuns em React Native: Página de Galeria

() translation by (you can also view the original English article)
Nessa série, aprendemos como criar layouts de páginas comuns, usados em apps mobile, com React Native. Os layouts não são funcionais—contudo, o foco da série é fazer-nos estruturar conteúdo em apps com React Native.
Se estiver começando a estruturar apps em React Native ou estilização em geral, veja nosso tutorial anterior:
Para acompanhar a série, temos o desafio de criar cada tela sozinhos, antes de ler as instruções passo-a-passo no tutorial. Não nos beneficiamos muito apenas lendo o tutorial! Tentemos primeiro antes de olhar as respostas. Se conseguir fazer parecer com a tela original, comparemos nossa solução com a do passo-a-passo. E então, decidamos qual é a melhor!
Nessa terceira publicação da série, criaremos uma página de galeria de fotos:



Galerias, no geral, são usadas para mostrar uma coleção de conteúdo relacionado onde apenas a informação necessária é apresentada. A maioria das vezes, isso inclui uma foto, título e outra informação relevante.
Eis alguns exemplos desse tipo de layout sendo usado por aí:






Configuração do Projeto
O primeiro passo, é configurar um novo projeto em React Native.
1 |
react-native init react-native-common-screens |
Com o projeto configurado, abramos index.android.js
e substituamos o conteúdo por isso:
1 |
import React, { Component } from 'react'; |
2 |
import { |
3 |
AppRegistry
|
4 |
} from 'react-native'; |
5 |
|
6 |
import Gallery from './src/pages/Gallery'; |
7 |
|
8 |
export default class ReactNativeCommonScreens extends Component { |
9 |
|
10 |
render() { |
11 |
return ( |
12 |
<Gallery /> |
13 |
);
|
14 |
}
|
15 |
|
16 |
}
|
17 |
|
18 |
AppRegistry.registerComponent('ReactNativeCommonScreens', () => ReactNativeCommonScreens); |
Criemos a pasta src/pages
e criemos o arquivo Gallery.js
dentro.
Também precisaremos do pacote react-native-vector-icons
. Usado especificamente para os ícones no rodapé.
1 |
npm install --save react-native-vector-icons |
Abramos o arquivo android/app/build.gradle
e adicionemos a referência ao pacote:
1 |
dependencies { |
2 |
//rest of the dependencies are here at the top |
3 |
compile project(':react-native-vector-icons') //add this |
4 |
} |
Façamos o mesmo em android/settings.gradle
, adicionando isso ao final do arquivo:
1 |
include ':react-native-vector-icons' |
2 |
project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android') |
Abramos android/app/src/main/java/com/react-native-common-screens/MainApplication.java
e importemos o pacote:
1 |
import java.util.Arrays; |
2 |
import java.util.List; |
3 |
|
4 |
import com.oblador.vectoricons.VectorIconsPackage; //add this |
Por último, uniciemo-no:
1 |
@Override
|
2 |
protected List<ReactPackage> getPackages() { |
3 |
return Arrays.<ReactPackage>asList( |
4 |
new MainReactPackage(), |
5 |
new VectorIconsPackage() //add this |
6 |
);
|
7 |
}
|
Criando a Página de Galeria
Certo, agora que tentamos criar o código por conta própria (sem trapaça, certo?), mostraremos nossa implementação.
Diferentes das páginas anteriores, a galeria precisa de algumas imagens que serviremos como conteúdo principal. Podemos ir no Google e pesquisar imagens ou usar as existentes no repositório do Github. Todas as imagens tem permissão de seus autores para serem usadas, logo podemos usá-las sem problemas. Com as imagens, salvemo-nas na pasta src/images
. Pelo jeito que as imagens serão apresentadas, todas devem ter dimensões iguais.
Comcemos criando src/pages/Gallery.js
e adicionando o código base:
1 |
import React, { Component } from 'react'; |
2 |
|
3 |
import { |
4 |
StyleSheet, |
5 |
View, |
6 |
ScrollView, |
7 |
Image, |
8 |
} from 'react-native'; |
9 |
|
10 |
import Icon from 'react-native-vector-icons/FontAwesome'; |
11 |
import Button from '../components/Button'; |
12 |
|
13 |
export default class Gallery extends Component { |
14 |
...
|
15 |
}
|
A página precisa de um constructor()
onde definiremos os caminhos das imagens que usaremos. No React Native, para referir a imagens localizadas no diretório do projeto, basta requerê-las como requeremos um módulo JavaScript. Vale notar que não podemos ter caminhos de imagens dinamicamente gerados, logo, temos de fornecer um caminho de verdade.
1 |
constructor(props) { |
2 |
super(props); |
3 |
this.state = { |
4 |
photos: [ |
5 |
{
|
6 |
label: 'beach', |
7 |
src: require('../images/beach.jpg') |
8 |
},
|
9 |
{
|
10 |
label: 'bridge', |
11 |
src: require('../images/bridge.jpg') |
12 |
},
|
13 |
{
|
14 |
label: 'fields', |
15 |
src: require('../images/fields.jpg') |
16 |
},
|
17 |
{
|
18 |
label: 'mountains', |
19 |
src: require('../images/mountains.jpg') |
20 |
},
|
21 |
{
|
22 |
label: 'sunflower', |
23 |
src: require('../images/sunflower.jpg') |
24 |
},
|
25 |
{
|
26 |
label: 'sunset', |
27 |
src: require('../images/sunset.jpg') |
28 |
},
|
29 |
{
|
30 |
label: 'lake', |
31 |
src: require('../images/lake.jpg') |
32 |
},
|
33 |
{
|
34 |
label: 'nature', |
35 |
src: require('../images/nature.jpg') |
36 |
},
|
37 |
{
|
38 |
label: 'pink', |
39 |
src: require('../images/pink.jpg') |
40 |
},
|
41 |
{
|
42 |
label: 'rails', |
43 |
src: require('../images/rails.jpg') |
44 |
},
|
45 |
]
|
46 |
};
|
47 |
}
|
Não precisamos defini-las agora, já que os valores não alterarão. Poderíamos definí-las em um arquivo separado, importá-lo e atribuir a uma variável, e usá-la diretamente. Mas, para simplificar, decidimos colocar tudo na propriedade state.
Dentro de render()
, não faremos como os outros, envolvendo tudo com um componente ScrollView
, porque o componente de abas da parte inerior da tela deve ter uma posição fixa. Isso significa que mesmo se as fotos passarem da altura disponível, as abas devem permanecer no lugar. Para conseguir isso, usemos um componente View
para envolver tudo e envolver apenas as fotos em um ScrollView
. Isso permite-nos aplicar a rolagem apenas ao container da coleção de fotos:
1 |
render() { |
2 |
return ( |
3 |
<View style={styles.container}> |
4 |
<ScrollView style={styles.gallery}> |
5 |
{ this.renderGallery() } |
6 |
</ScrollView> |
7 |
<View style={styles.tabs}> |
8 |
</View> |
9 |
);
|
10 |
}
|
Agora, começamos a ver um padrão. Toda vez que precisamos usar JavaScript dentro de render()
, devemos criar uma função separada para esse código ao invés de colocá-lo diretamente dentro de render()
. Isso mantém simples e limpo.
Agora, sigamos para a estilização. Embora uma ScrollView
não seja usada para envolver tudo, agora, é important enotar que ainda precisamos usar flex: 1
no container principal para ele tomar todo o espaço disponível.
1 |
container: { |
2 |
flex: 1, |
3 |
flexDirection: 'column' |
4 |
},
|
5 |
gallery: { |
6 |
flexDirection: 'column' |
7 |
},
|
8 |
tabs: { |
9 |
flexDirection: 'row', |
10 |
backgroundColor: '#333', |
11 |
padding: 20 |
12 |
},
|
13 |
tab: { |
14 |
flex: 1 |
15 |
},
|
16 |
icon: { |
17 |
textAlign: 'center' |
18 |
},
|
renderGallery()
é bem parecida com renderWeeks()
que usamos no tutorial anterior, usada para renderizar a página do calendário. Se quiser relembrar como funciona, vá em frente e veja o tutorial anterior sobre páginas de calendário. O que é preciso saber é que resizeMode
é aplicado em Image
. Nesse caso, usamos cover
, que faz a imagem ocupar todo o espaço disponível em seu container, e mantendo a proporção. Isso faz as imagens estourarem um pouco em aparelhos com telas maiores se a imagem for menor.
1 |
renderGallery() { |
2 |
var count = 0; |
3 |
var previous_item = ''; |
4 |
var pairs = this.getPairsArray(this.state.photos); |
5 |
return pairs.map((item, index) => { |
6 |
return ( |
7 |
<View style={styles.item} key={index}> |
8 |
<Image |
9 |
resizeMode={Image.resizeMode.cover} |
10 |
style={styles.photo} |
11 |
source={item[0].src} /> |
12 |
<Image |
13 |
resizeMode={Image.resizeMode.cover} |
14 |
style={styles.photo} |
15 |
source={item[1].src} /> |
16 |
</View> |
17 |
);
|
18 |
});
|
19 |
}
|
Eis getPairsArray()
:
1 |
getPairsArray(photos) { |
2 |
var pairs_r = []; |
3 |
var pairs = []; |
4 |
var count = 0; |
5 |
photos.forEach((item) => { |
6 |
count += 1; |
7 |
pairs.push(item); |
8 |
if(count == 2){ |
9 |
pairs_r.push(pairs) |
10 |
count = 0; |
11 |
pairs = []; |
12 |
}
|
13 |
});
|
14 |
return pairs_r; |
15 |
}
|
Por fim, eis o estilo para cada linha (item
) e foto (photo
). Notemos o uso de flex: 1
na foto. Isso é porque o componente Image
é seu próprio container. Queremos que o container em si ocupe metade do espaço disponível para cada linha—é por isso que uma propriedade flex
deve ser usada. Se não for feito, apenas as dimensões requeridas pela foto serão usadas e resizeMode
, adicionada anteriormente, não entrará em efeito.
1 |
item: { |
2 |
flex: 1, |
3 |
flexDirection: 'row', |
4 |
},
|
5 |
photo: { |
6 |
flex: 1 |
7 |
}
|
Conclusão
É isso! Nesse tutorial, aprendemos como implementar layout para uma página de galeria. Focamos em como lidar com imagens ao estruturar apps com React Native. Geralmente, usaremos uma combinação de flex
e resizeMode
para fazer as imagens seguirem do jeito que queremos. Como sua solução compara com a nossa? Diga-nos deixando uma comentário abaixo.
No próximo tutorial, aprenderemos como implementar um layout muito usado em apps de notícias Enquanto isso, veja alguns outros tutoriais sobre React Native e Flexbox!