Flutter

[Flutter] table_calendar 패키지로 Calendar 구현하기

ima9ine4 2024. 4. 2. 21:04
728x90

table_calendar 패키지를 사용해서 Calendar를 구현하기 위해, 개발자의 깃헙 베이직 코드를 뜯어보자.

Table Calendar 패키지 설치

table_calendar는 터미널에 아래 명령어를 입력하여 사용할 수 있다. 패키지가 잘 설치되면 pubspec.yaml 파일에 dependencies가 추가될 것이다.

flutter pub add table_calendar

 

Table Calendar 예제 코드

개발자의 Github에서 baisic_example.dart 파일이 있는 링크는 아래와 같다.

 

table_calendar/example/lib/pages/basics_example.dart at master · aleksanderwozniak/table_calendar

Highly customizable, feature-packed calendar widget for Flutter - aleksanderwozniak/table_calendar

github.com

▲ table_calendar pakage example code

아래는 전체 코드인데 주석을 조금 더 달아서 설명을 붙여놓았다.

📍 전체 코드 !!

// Copyright 2019 Aleksander Woźniak
// SPDX-License-Identifier: Apache-2.0

import 'package:flutter/material.dart';
import 'package:table_calendar/table_calendar.dart';

class TableBasicsExample extends StatefulWidget {
  @override
  _TableBasicsExampleState createState() => _TableBasicsExampleState();
}

class _TableBasicsExampleState extends State<TableBasicsExample> {
  CalendarFormat _calendarFormat = CalendarFormat.month;
  //CalendarFormat은 현재 캘린더의 형식을 나타낸다. 초기 값으로 월 형식을 사용한다.
  DateTime _focusedDay = DateTime.now();
  //사용자가 선택한 날짜를 나타낸다. 초기값으로 현재 날짜를 사용한다.
  DateTime? _selectedDay;
  //사용자가 선택한 날짜를 나타낸다. 초기값은 null이다.

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('TableCalendar - Basics'),
      ),
      body: TableCalendar(
        firstDay: kFirstDay,
        lastDay: kLastDay,
        focusedDay: _focusedDay,
        calendarFormat: _calendarFormat,
        selectedDayPredicate: (day) {
          // Use `selectedDayPredicate` to determine which day is currently selected.
          // If this returns true, then `day` will be marked as selected.
          //선택된 날짜를 결정하는 함수이다. _selectedDay와 'day'를 비교하여 선택된 날짜가 '날짜'인지 판단한다.

          // Using `isSameDay` is recommended to disregard
          // the time-part of compared DateTime objects.
          return isSameDay(_selectedDay, day);
        },
        onDaySelected: (selectedDay, focusedDay) {
        //사용자가 날짜를 선택했을 때 호출되는 콜백 함수이다.
          if (!isSameDay(_selectedDay, selectedDay)) {
          //selectedDay는 사용자가 선택한 날짜, focusedDay는 현재 포커스 되어 있는 날짜이다.
            // Call `setState()` when updating the selected day
            setState(() {
              _selectedDay = selectedDay;
              _focusedDay = focusedDay;
            });
          }
        },
        onFormatChanged: (format) {
        //캘린더 형식이 변경될 때 호출되는 콜백 함수이다.
        //나는 캘린더 형식을 월 단위로만 고정하여 사용할 것이므로 이 함수를 사용하지 않을 것이다.
          if (_calendarFormat != format) {
            // Call `setState()` when updating calendar format
            setState(() {
              _calendarFormat = format;
            });
          }
        },
        onPageChanged: (focusedDay) {
        //페이지가 변경될 때 호출되는 콜백 함수이다.
        //포커스된 날짜를 업데이트 한다. setState()를 호출할 필요가 없다.
          // No need to call `setState()` here
          _focusedDay = focusedDay;
        },
      ),
    );
  }
}

얼추 이해가 가는데.. 나는 onDaySelected 함수가 잘 이해가지 않았다...
selectedDay랑 focusedDay가 같은 값인 것 같은데 왜 변수를 나눠서 사용하는 것인지..?

일단 지피티 피셜은 아래와 같다. 

  • selectedDay: 이는 사용자가 직접 선택한 날짜를 나타냅니다. 사용자가 캘린더에서 특정 날짜를 탭하거나 선택할 때 이 값을 업데이트합니다.
  • focusedDay: 이는 현재 사용자가 캘린더에서 포커스를 맞추고 있는 날짜를 나타냅니다. 사용자가 달력 위에서 스와이프하여 다른 날짜를 보고 있을 때 이 값이 변경됩니다.

이 두 값은 서로 다르며, 다음과 같은 상황에서 차이를 보일 수 있다.

  • 사용자가 캘린더에서 다른 달로 스와이프하여 새로운 달의 날짜를 볼 때, focusedDay는 새로운 달의 첫 날을 나타낸다. 그러나 selectedDay는 사용자가 직접 선택한 날짜가 유지된다.
  • 사용자가 달력에서 다른 날짜를 선택하면, selectedDay만 해당 날짜로 업데이트되고 focusedDay는 변경되지 않는다.

이 내용이 맞는지 직접 확인해보기 위해 변수를 화면에 출력해보았다.
TableCalender를 Column위젯으로 감싸고, 아래에 Text위젯을 추가해주었다. 또한 페이지가 변경되었을 때도 focusedDay 값을 업데이트 해주기 위해 onPageChanged함수 안에 setState함수를 추가해주었다.

Text('SelectedDay : ${_selectedDay.toString()};'),
Text('focusedDay : ${_focusedDay.toString()};'),
onPageChanged: (focusedDay) {
              setState(() {
                _focusedDay = focusedDay;
              });
            },

그랬더니 첫 화면은 아래와 같다. 넣어준 초기값대로 SelectedDay는 null, focusedDay는 현재 날짜로 설정되었다.


여기에서 4월 19일을 선택해보자. selectedDay는 사용자가 직접 선택한 날짜를 의미하므로 4월 19일로 올바르게 변경되었다. 

왜 이렇게 됐을까? 사용자가 4월 19일 날짜를 클릭하니 'onDaySelected' 콜백 함수가 호출되었다. 참고로 이 함수는TableCalendar위젯 내부에서 관리되고 제공되는 함수라고 한다. 따라서 이 함수가 전달하는 2개의 인자 'selectedDay'와 'focusedDay'는 우리가 코드를 짤 때 수동으로 제공할 필요가 없다. TableCalendar가 사용자의 선택에 따라 넣어서 전달해주는 것 같다. 그래서 두 변수 다 4월 19일로 설정되었다.


이 상태에서 다음 달로 페이지를 넘긴다면? selectedDay는 유지된 채, focusedDay만 5월 1일로 바뀌었다.

이는 onPageChanged 함수 안에서 focusedDay의 값을 다시 설정해주었기 때문이다. focusedDay는 현재 캘린더가 화면 상에서 보이는 범위 내에서 포커스를 맞추고 있는 날짜를 의미한다. 페이지를 넘겼더니 focusedDay의 값이 5월 1일로 변경된 이유는 5월 캘린더 화면 중에서 제일 중요한 날짜가 첫 번째 날로 설정되어 있기 때문일 것이다. 어떤 것을 focusedDay값으로 둘 것인지는 필요에 따라 변경할 수 있다. 예를 들면 매월 첫 번째 날이 아닌, 가운데 날짜로 설정할 수도 있다.


근데 페이지를 전환할 때마다 focusedDay값을 매번 업데이트 해줄 필요는 없다. 내부적으로만 처리해주면 되고 화면에 다시 그릴 필요는 없기 때문이다. 어차피 사용자가 선택한 값만 화면에서 보여주면 문제 없기 때문이다! 그래서 onPageChanged()에서 setState를 사용할 필요가 없다고 한 것이었다. onPageChanged() 함수에서 다시 setState를 하지 않으면 어떻게 될까?

4월 19일을 선택하고, 5월로 페이지를 넘겼는데도 focusedDay는 여전히 4월 19일이다. 변하지 않았다. 

 

 

728x90
반응형