Utilizando react-native-calendars na prática
Marcos Willian
Posted on June 3, 2024
React Native Calendars é um componente de calendário para iOS e Android. Ele oferece uma variedade de funcionalidades e é completamente escrito em JavaScript, o que significa que não há necessidade de código nativo.
Neste tutorial, vamos recriar no React Native uma tela que encontrei no Dribbble. Este design combina estilo e funcionalidade, e você pode conferir o design original aqui. Vamos explorar algumas características do React Native Calendars e ver como podemos usá-lo para criar uma interface de usuário atraente e eficiente.
Pré-requisitos
Para me acompanhar nesse projeto não é necessário muito, basta ter o NodeJS instalado.
Iniciando o projeto
Nesse projeto vou estar utilizando o Expo, caso você não conheça, segundo a própria documentação do React Native, ele é um framework de React Native que oferece ferramentas de desenvolvimento que facilitam a criação de aplicativos, como uma biblioteca padrão de módulos nativos e muito mais.
Vamos começar criando um projeto React Native utilizando o seguinte comando no terminal:
$ npx create-expo-app --template blank
Caso você receba esse prompt perguntando o nome do seu app, preferencialmente não utilize nenhum acento ou caractere especial.
? What is your app named? calendars-example
Após ter instalado todas as dependências necessárias, vamos instalar a biblioteca react-native-calendars:
Primeiro, através do terminal, navegue até a pasta onde está seu projeto, e depois digite o comando abaixo:
$ npm install --save react-native-calendars
Com isso já podemos recriar o design do dribble. Para estilizar vou estar utilizando o próprio StyleSheet do React Native, mas você pode utilizar o que estiver mais acostumado, seja styled-components ou native-wind.
Abra o projeto no seu editor de código favorito, eu vou estar utilizando o VSCode. Vou começar removendo todo o código existente no arquivo App.js e adicionando apenas um titulo para nosso calendário:
Agora vamos rodar o projeto e ver como está ficando, para rodar basta digitar no terminal o comando abaixo (não esquece q tem que estar na pasta do projeto 😁):
$ npx expo start
Você vai se deparar com um QRCode que irá aparecer no seu terminal, basta escanear ele com o seu celular, mas antes, para rodar o aplicativo você vai precisar instalar o app Expo Go no seu celular, que é um aplicativo do Expo para rodar seus projetos criados com o Framework.
Após ter escaneado, e o app ter rodado, você deve estar vendo uma tela parecida com essa abaixo:
Vamos continuar, agora criando a parte mais legal que é o calendário em si:
- Primeiro, vamos adicionar o componente de Calendário da biblioteca, e fazer pequenas estilizações
import { StyleSheet, Text, SafeAreaView, View } from 'react-native';
import { Calendar, CalendarUtils } from 'react-native-calendars';
const INITIAL_DATE = CalendarUtils.getCalendarDateString(new Date());
export default function App() {
return (
<>
// ✂️ Código comentado apenas para melhor visualização
<SafeAreaView style={styles.container}>
// ✂️ Código comentado apenas para melhor visualização
<View style={styles.wrapper}>
<Calendar
current={INITIAL_DATE}
style={styles.calendar}
/>
</View>
</SafeAreaView>
</>
);
}
const styles = StyleSheet.create({
// ✂️ Código comentado apenas para melhor visualização
wrapper: {
paddingHorizontal: 24
},
calendar: {
borderRadius: 24,
overflow: 'hidden',
}
});
Caso você esteja se perguntando o que essa linha faz:
const INITIAL_DATE = CalendarUtils.getCalendarDateString(new Date());
Basicamente eu estou utilizando uma função auxiliar da própria biblioteca que transforma uma data no formato 'YYYY-MM-DD', que é o formato que o componente espera.
- Agora vamos terminar de estilizar nosso Calendário, mudando as cores default do tema e adicionando arrows customizadas para ficar mais parecido com o design:
import { StyleSheet, Text, SafeAreaView, View } from 'react-native';
import { Calendar, CalendarUtils } from 'react-native-calendars';
import { Feather } from '@expo/vector-icons';
const INITIAL_DATE = CalendarUtils.getCalendarDateString(new Date());
export default function App() {
return (
<>
// ✂️ Código comentado apenas para melhor visualização
<SafeAreaView style={styles.container}>
// ✂️ Código comentado apenas para melhor visualização
<View style={styles.wrapper}>
<Calendar
current={INITIAL_DATE}
style={styles.calendar}
theme={{
todayTextColor: '#6567ef',
textMonthFontWeight: 'bold',
textDayHeaderFontWeight: 'bold',
textMonthFontSize: 18,
textDayFontWeight: 'bold',
textDayFontSize: 14,
selectedDayBackgroundColor: '#6567ef',
selectedDayTextColor: '#ffffff',
}}
renderArrow={direction => {
return (
<View style={styles.calendarButton}>
<Feather name={`chevron-${direction}`} size={24} color="#6567ef" />
</View>
)
}}
/>
</View>
</SafeAreaView>
</>
);
}
const styles = StyleSheet.create({
// ✂️ Código comentado apenas para melhor visualização
wrapper: {
paddingHorizontal: 24
},
calendar: {
borderRadius: 24,
overflow: 'hidden',
}
calendarButton: {
backgroundColor: '#e9e5fe',
padding: 8,
borderRadius: 100,
}
});
- Para finalizar nosso calendário, vamos adicionar um estado para salvar a data selecionada e mostrar ela no componente:
import { useState } from 'react';
// ✂️ Código comentado apenas para melhor visualização
export default function App() {
const [selected, setSelected] = useState(INITIAL_DATE);
function onDayPress(day) {
setSelected(day.dateString);
}
const marked = {
[selected]: {
selected: true,
}
};
return (
<>
// ✂️ Código comentado apenas para melhor visualização
<SafeAreaView style={styles.container}>
// ✂️ Código comentado apenas para melhor visualização
<View style={styles.wrapper}>
<Calendar
// ✂️ Código comentado apenas para melhor visualização
onDayPress={onDayPress}
markedDates={marked}
/>
</View>
</SafeAreaView>
</>
);
}
// ✂️ Código comentado apenas para melhor visualização
Pronto! seu calendário deve estar parecido com esse:
Agora vamos colocar a parte de "Available Time" e um botão para confirmar o agendamento, o código completo dessa UI você pode ver abaixo:
import { useState } from 'react';
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, SafeAreaView, View, Pressable, ScrollView } from 'react-native';
import { Calendar, CalendarUtils } from 'react-native-calendars';
import { Feather } from '@expo/vector-icons';
const INITIAL_DATE = CalendarUtils.getCalendarDateString(new Date());
const AVAILABLE_TIMES = [
{ id: 1, hour: '07:00 PM', status: 'available' },
{ id: 2, hour: '07:15 PM', status: 'unavailable' },
{ id: 3, hour: '07:30 PM', status: 'available' },
{ id: 4, hour: '08:30 PM', status: 'unavailable' },
{ id: 5, hour: '08:45 PM', status: 'available' },
{ id: 6, hour: '09:00 PM', status: 'available' },
]
export default function App() {
const [selected, setSelected] = useState(INITIAL_DATE);
const [timeSelected, setTimeSelected] = useState(null);
function onDayPress(day) {
setSelected(day.dateString);
}
const marked = {
[selected]: {
selected: true,
}
};
return (
<>
<StatusBar style="auto" />
<SafeAreaView style={styles.container}>
<ScrollView>
<Text style={styles.title}>Appointment Booking</Text>
<View style={styles.wrapper}>
<Calendar
theme={{
todayTextColor: '#6567ef',
textMonthFontWeight: 'bold',
textDayHeaderFontWeight: 'bold',
textMonthFontSize: 18,
textDayFontWeight: 'bold',
textDayFontSize: 14,
selectedDayBackgroundColor: '#6567ef',
selectedDayTextColor: '#ffffff',
}}
enableSwipeMonths
current={INITIAL_DATE}
minDate={INITIAL_DATE}
style={styles.calendar}
onDayPress={onDayPress}
markedDates={marked}
renderArrow={direction => {
return (
<View style={styles.calendarButton}>
<Feather name={`chevron-${direction}`} size={24} color="#6567ef" />
</View>
)
}}
/>
<Text style={styles.sectionTitle}>Available Time</Text>
<View style={styles.availableTimeList}>
{AVAILABLE_TIMES.map(({ id, hour, status }) => {
const isTimeEnabled = status === 'available'
const isTimeSelected = timeSelected === hour
const defaultBg = isTimeEnabled ? '#e9e5fe' : '#ebe9f6'
const defaultColor = isTimeEnabled ? '#222222' : '#aeaeb0'
return (
<Pressable
key={id}
style={[
styles.availableTimeButton,
{ backgroundColor: isTimeSelected ? '#6567ef' : defaultBg }
]}
onPress={() => setTimeSelected(hour)}
disabled={!isTimeEnabled}
>
<Text
style={[
styles.availableTimeButtonText,
{ color: isTimeSelected ? '#ffffff' : defaultColor }
]}>
{hour}
</Text>
</Pressable>
)
})}
</View>
<Pressable style={styles.ConfirmScheduleButton}>
<Text style={styles.ConfirmScheduleButtonText}>Confirm Schedule</Text>
</Pressable>
</View>
</ScrollView>
</SafeAreaView>
</>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f6fa',
},
title: {
marginHorizontal: 'auto',
fontWeight: 'bold',
marginVertical: 24,
fontSize: 18,
},
wrapper: {
paddingHorizontal: 24
},
calendar: {
borderRadius: 24,
overflow: 'hidden',
},
calendarButton: {
backgroundColor: '#e9e5fe',
padding: 8,
borderRadius: 100,
},
sectionTitle: {
fontWeight: 'bold',
marginVertical: 24,
fontSize: 16,
},
availableTimeList: {
flexDirection: 'row',
flexWrap: 'wrap',
gap: 12,
marginBottom: 64,
},
availableTimeButton: {
width: '30%',
marginBottom: 6,
paddingVertical: 16,
borderRadius: 999,
alignSelf: 'flex-start',
alignItems: 'center'
},
availableTimeButtonText: {
fontWeight: 'bold',
fontSize: 12
},
ConfirmScheduleButton: {
backgroundColor: '#6567ef',
padding: 22,
borderRadius: 999,
alignItems: 'center'
},
ConfirmScheduleButtonText: {
fontSize: 16,
fontWeight: 'bold',
color: '#ffffff'
}
});
Sua tela final deve estar parecida com essa abaixo:
Espero que tenha gostado, você pode acessar aqui o repositório com o código final caso esteja tendo dificuldades.
Sinta-se à vontade para sugerir uma biblioteca especifica para mim construir algo utilizando o Dribble. Obrigado!
Posted on June 3, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.