DEV Community

Cover image for Utilizando react-native-calendars na prática
Marcos Willian
Marcos Willian

Posted on

Utilizando react-native-calendars na prática

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:

tela do vscode com código react-native

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:

celular iphone mostrando um app no expo go

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',
  }
});
Enter fullscreen mode Exit fullscreen mode

Caso você esteja se perguntando o que essa linha faz:

const INITIAL_DATE = CalendarUtils.getCalendarDateString(new Date());
Enter fullscreen mode Exit fullscreen mode

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,
  }
});
Enter fullscreen mode Exit fullscreen mode
  • 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
Enter fullscreen mode Exit fullscreen mode

Pronto! seu calendário deve estar parecido com esse:

celular iphone mostrando um app no expo go com um calendário

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'
  }
});
Enter fullscreen mode Exit fullscreen mode

Sua tela final deve estar parecida com essa abaixo:

celular iphone mostrando um app no expo go com um calendário

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!

Sentry blog image

How to reduce TTFB

In the past few years in the web dev world, we’ve seen a significant push towards rendering our websites on the server. Doing so is better for SEO and performs better on low-powered devices, but one thing we had to sacrifice is TTFB.

In this article, we’ll see how we can identify what makes our TTFB high so we can fix it.

Read more

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay