Nessa aula você irá aprender a criar temas usando styled-components e estilos globais. Em aplicativos é muito comum que tenhamos um padrão de estilização e para lidar com isso podemos usar o próprio styled-components que você já aprendeu. Além disso, você ainda irá ver como é fácil utilizar fontes customizadas nos seus aplicativos com o Expo.
O styled-components possui um componente especial chamado ThemeProvider, ele utiliza a Context API do React para passar as propriedades para dentro da árvore de componentes.
Para começar a criar o tema precisamos definir um objeto com as propriedades que utilizaremos no ThemeProvider, colors e fonts. Vamos importar o ThemeProvider, criar um objeto theme no componente principal App.js para utilizarmos no ThemeProvider e então colocá-lo em volta de todos os componentes onde queremos o tema:
Obs.: você pode estruturar a propriedade theme da forma que preferir.
import StackNavigator from "./src/StackNavigator";
import { ThemeProvider } from "styled-components";
const theme = {
colors: {
red: '#F64348',
dark: '#1C1A1D',
light: '#EAEAEA'
},
fonts: {}
}
export default function App() {
return (
<ThemeProvider theme={theme}>
<StackNavigator />
</ThemeProvider>
)
}
Tudo pronto! Já podemos ver nosso tema em ação utilizando a prop theme que ficará disponível em todos os componentes criados com o styled. Vamos modificar os componentes StyledContainer e StyledTitle para usarem o tema:
StyledContainer.js
import styled from 'styled-components/native'
export default styled.View`
flex: 1;
background-color: ${(props) => props.theme.colors.red};
margin: 32px 0 0;
padding: 16px;
`
StyledTitle.js
import styled from "styled-components/native";
export default styled.Text`
color: ${(props) => props.theme.colors.dark};
font-size: 40px;
`
Vamos atualizar também o componente StyledButton. Veja como é fácil atualizar os estilos quando utilizamos um tema globalmente dessa forma:
import styled from "styled-components/native";
const Background = styled.TouchableOpacity`
background-color: ${({ theme }) => theme.colors.dark};
border-radius: 10px;
margin: 10px 0;
padding: 10px;
`
const Text = styled.Text`
color: ${({ theme }) => theme.colors.light};
`
export default function ({ bgColor, color, children, onPress }) {
return (
<Background color={bgColor} onPress={onPress}>
<Text color={color}>
{children}
</Text>
</Background>
)
}
Agora vamos ver como utilizar as fontes através do expo-font. Para começar, vamos instalar o expo-font e uma fonte do Google Fonts (https://fonts.google.com/). Eu irei instalar a Roboto, mas você pode instalar outro que preferir modificando o comando:
npx expo install expo-font @expo-google-fonts/roboto
Com a fonte instalada vamos usar o hook do expo-font useFonts, que serve para carregarmos as fontes. Também iremos importar a variação 700 (negrito) da Roboto e usá-la, assim será fácil perceber a mudança no aplicativo:
Obs.: estamos usando uma fonte do Google Fonts aqui por uma questão de facilidade, pois o Expo nos disponibiliza todas as fontes disponíveis lá, mas poderíamos usar tranquilamente nossas próprias fontes através de arquivos OTF ou TTF (outros formatos são possíveis, porém estes dois são universalmente compatíveis com Andorid, iOS e web)
import StackNavigator from "./src/StackNavigator";
import { ThemeProvider } from "styled-components";
import { useFonts } from "expo-font";
import { Roboto_700Bold } from "@expo-google-fonts/roboto";
const theme = {
colors: {
red: '#F64348',
dark: '#1C1A1D',
light: '#EAEAEA'
},
fonts: {}
}
export default function App() {
const [fontsLoaded] = useFonts({ roboto: Roboto_700Bold })
return (
<ThemeProvider theme={theme}>
<StackNavigator />
</ThemeProvider>
)
}
Agora que já carregamos a fonte basta incluí-la no arquivo theme.js:
Obs.: repare que estamos usando esse nome simplesmente por ser o formato da biblioteca @expo-google-fonts/roboto e porque o passamos como parâmetro no useFonts, para usar outros nomes poderíamos fazer isso passando um nome diferente no parâmetro do useFonts.
export const theme = {
colors: {
red: '#F64348',
dark: '#1C1A1D',
light: '#EAEAEA'
},
fonts: {
roboto700: 'Roboto_700Bold'
}
}
E para que ela seja utilizada por um componente podemos usar a propriedade font-family. Vamos ver o resultado mudando a fonte do componente StyledTitle:
import styled from "styled-components/native";
export default styled.Text`
color: ${(props) => props.theme.colors.dark};
font-family: ${({ theme }) => theme.fonts.roboto700};
font-size: 40px;
`
Agora vamos fazer uma mudança interessante na nossa tela principal HomeScreen, vamos incluir o StyledTitle nela para utilizarmos a fonte Roboto na primeira tela do aplicativo:
// ...
import NavButton from '../components/NavButton';
import StyledTitle from '../components/StyledTitle';
// ...
return (
<Container>
<StyledTitle>Olá, mundo!</StyledTitle>
<StatusBar style="auto" />
<NavButton text="Aula de Navegação" onPress={toNavigationScreen} />
<NavButton text="Aula de ScrollView" onPress={toScrollViewScreen} />
<NavButton text="Aula de FlatList" onPress={toFlatListScreen} />
<NavButton text="Aula de Styled Components" onPress={toStyledComponentsScreen} />
</Container>
)
}
Repare que agora nosso aplicativo emitiu um erro informando que a fonte Roboto não existe no dispositivo. Isso acontece porque no Android e no iOS, assim como nos desktops, o aplicativo irá procurar entre as fontes instaladas no sistema, mas como essa é a primeira tela a nossa fonte ainda não foi carregada (trata-se de um processo assíncrono).
Para contornar esse problema podemos criar uma SplashScreen, que é aquela tela de abertura do aplicativo. Com ela podemos atrasar a inicialização do aplicativo até que nossa fonte esteja carregada. Faremos isso usando o expo-splash-screen. Instale-o com o comando a seguir:
npx expo install expo-splash-screen
Agora que instalamos essa biblioteca vamos usá-la para atrasar a inicialização até o carregamento da fonte:
// ...
import { Roboto_700Bold } from "@expo-google-fonts/roboto";
import * as SplashScreen from 'expo-splash-screen';
import { useCallback } from "react";
import { View } from "react-native";
// importante para prevenir que a SplashScreen se
// encerre automaticamente antes da fonte ser carregada
SplashScreen.preventAutoHideAsync()
export default function App() {
const [fontsLoaded] = useFonts({ Roboto_700Bold })
// esconde a SplashScreen assim que fontsLoaded for modificado,
// ou seja, quando a fonte for carregada
const onLayoutRootView = useCallback(async () => {
if (fontsLoaded) {
await SplashScreen.hideAsync()
}
}, [fontsLoaded])
if (!fontsLoaded) {
return null // impede a renderização até que a fonte carregue
}
// usamos uma <View> ocupando toda a tela
// para ter acesso ao evento onLayout
return (
<View style={{ flex: 1 }} onLayout={onLayoutRootView}>
<ThemeProvider theme={theme}>
<StackNavigator />
</ThemeProvider>
</View>
)
}