본문 바로가기
Flutter

[Flutter] api event 수신하여 table_calendar 달력 그리기

by hymndaniel 2022. 4. 22.

table_calendar를 사용할때 중요한 요소들만 정리. 나머지는 공식문서 참조

기본적인 코드의 구성은 문서에서 제공하는 git repo를 참고하였다.

https://github.com/aleksanderwozniak/table_calendar/blob/master/example/lib/pages/events_example.dart

 

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

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

github.com

 

예시에 나와있는 방식에서 변형한 것은 api에서 달력에 표시할 event data를 받아온 뒤에 event map을 eventLoader에서 사용할 수 있게하는 것이다.

 

* eventLoader는 _getEventsForDay 메서드를 사용한다.

* _getEventsForDay 메서드는 events라는 Map을 참조한다. 

* events는 LinkedHashMap을 사용하여 {"날짜" : [이벤트1, 이벤트2], ...} 구조로 이벤트 정보를 가지게 한다.

 

event 초기화

Map<DateTime, dynamic> events = {};

 

해당일의 이벤트를 가져오고 없으면 빈 리스트를 반환한다.

  List<Event> _getEventsForDay(DateTime day) {
    return events[day] ?? [];
  }

 

event를 처리할 Model 생성

class Event {
  final String title;

  const Event(this.title);

  @override
  String toString() => title;
}

 

dio 패키지를 통해 api와 통신, events 에 넣어줄 eventSource를 준비해주는 메서드.

eventSource에 {"event발생 일" :[ "이벤트 명" ]}구조로 추가해주는 것입니다.

Future<Map<DateTime, dynamic>> fetchEventData(date) async {
    var dio = Dio();
    String url = serverURL! + routeName;
    var formData = FormData.fromMap({
      "engn_id": engnId,
      "user_id": widget.userId,
      "date": date,
      "type": 'month',
    });
    final response = await dio.post(url, data: formData);
    var data = response.data['data'];
    Map<DateTime, dynamic> eventSource = {};
    for (int i = 0; i < data.length; i++) {
      DateTime date = DateTime.parse(data[i]['opert_de']); //작업 날짜를 날짜형식으로 처리
      print(date);
      eventSource[date] = [Event('농작업')];
    }
    return eventSource;
  }

 

준비된 eventSource가 LinkedHashMap에 추가되도록하며, fetchEventData가 비동기로 처리되도록 합니다.

다 받아진 후에 화면(달력의 셀에 표시하는 UI, 그 날에 해당하는 이벤트 리스트)을 다시 그려주기 위해 setState에 이벤트를 갱신해줍니다.

  void updateEvent(date) async {
    dynamic hashMap = LinkedHashMap(
      equals: isSameDay,
      hashCode: getHashCode,
    )..addAll(await fetchEventData(date));
    setState(() {
      events = hashMap;
      _selectedEvents = ValueNotifier(_getEventsForDay(_selectedDay!)); //api 통신 이후에 선택된 작업 리스트 다시 그려줌
    });
  }

 

initState에서 updateEvent 메서드 실행.

@override
  void initState() {
    super.initState();
    updateEvent(initDate);
    _selectedDay = _focusedDay;
    _selectedEvents = ValueNotifier(_getEventsForDay(_selectedDay!));
  }

 

달력을 그려주는 위젯

신경썼던 부분은 다른 월로 이동할때 focus가 바뀌는 것, 이번달로 돌아왔을때 오늘을 focus 하는 것이다.

  Widget _buildTableCalendar() {
    return TableCalendar(
      calendarBuilders: CalendarBuilders(
        markerBuilder: (context, day, events) {
          if (events.isEmpty) return SizedBox();
          return ListView.builder(
            scrollDirection: Axis.horizontal,
            shrinkWrap: true,
            itemCount: events.length,
            itemBuilder: (context, index) {
              return Container(
                margin: const EdgeInsets.only(top: 30),
                width: 5,
                decoration: BoxDecoration(shape: BoxShape.circle, color: _getColorByEvent(events[index].toString())),
              );
            },
          );
        },
        selectedBuilder: (context, date, events) => Container(
          margin: const EdgeInsets.all(4.0),
          alignment: Alignment.center,
          decoration: BoxDecoration(color: Colors.black26, borderRadius: BorderRadius.circular(10.0)),
          child: Text(
            date.day.toString(),
            style: TextStyle(color: Colors.white),
          ),
        ),
        todayBuilder: (context, date, events) => Container(
          margin: const EdgeInsets.all(4.0),
          alignment: Alignment.center,
          decoration: BoxDecoration(color: Colors.black12, borderRadius: BorderRadius.circular(10.0)),
          child: Text(
            date.day.toString(),
            style: TextStyle(color: Colors.white),
          ),
        ),
      ),
      locale: 'ko-KR',
      headerStyle: HeaderStyle(
        headerPadding: EdgeInsets.zero,
        titleTextFormatter: (date, locale) => DateFormat.yMMMM(locale).format(date),
        titleCentered: true,
        formatButtonVisible: false,
        titleTextStyle: TextStyle(
          color: Colors.black,
          fontSize: 20,
          fontFamily: 'NanumSquareOTF',
          fontWeight: FontWeight.w700,
        ),
      ),
      calendarStyle: CalendarStyle(
        outsideDaysVisible: true,
        // holidayTextStyle: const TextStyle(color: const Color(0xFF5C6BC0)),
        // weekendTextStyle: TextStyle(color: Colors.red),
      ),
      firstDay: kFirstDay,
      lastDay: kLastDay,
      focusedDay: _focusedDay,
      selectedDayPredicate: (day) => isSameDay(_selectedDay, day),
      rangeStartDay: _rangeStart,
      rangeEndDay: _rangeEnd,
      calendarFormat: _calendarFormat,
      rangeSelectionMode: _rangeSelectionMode,
      eventLoader: _getEventsForDay,
      startingDayOfWeek: StartingDayOfWeek.sunday,
      onDaySelected: _onDaySelected,
      onRangeSelected: _onRangeSelected,
      onPageChanged: (focusedDay) {
        var thisMonth = DateFormat('MM').format(DateTime.now()); // ex) 04
        var movedMonth = DateFormat('MM').format(focusedDay); // ex) 04
        _focusedDay = focusedDay;
        var requestDate = DateFormat('yyyy-MM').format(focusedDay);
        setState(
          () {
            // 해당월 페이지에서는 선택날짜를 오늘로 지정
            if (movedMonth == thisMonth) {
              updateEvent(requestDate);
              _selectedDay = DateTime.now();
              _selectedEvents.value = _getEventsForDay(DateTime.now()); //다른 월로 넘어가면 선택일과 작업리스트를 바꿔줘야 함
            } else {
              // 이번달이 아닌 페이지에서는 1일이 선택
              updateEvent(requestDate);
              _selectedDay = focusedDay;
              _selectedEvents.value = _getEventsForDay(focusedDay); //다른 월로 넘어가면 선택일과 작업리스트를 바꿔줘야 함
              // _selectedEvents = ValueNotifier(_getEventsForDay(_selectedDay!));
            }
          },
        );
      },
    );
  }

 

달력 밑에 그날의 작업을 그려주는 위젯

Widget _buildWorkRow() {
    return ValueListenableBuilder<List<Event>>(
      valueListenable: _selectedEvents,
      builder: (context, value, _) {
        if (value.isEmpty) return const SizedBox();
        return InkWell(
          onTap: () {
            DayPopup.open(context);
          },
          child: Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: value
                .map(
                  (item) => Padding(
                    padding: const EdgeInsets.symmetric(horizontal: 3),
                    child: Container(
                      child: Padding(
                        padding: const EdgeInsets.all(8.0),
                        child: Row(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: [
                            Text(item.toString()),
                          ],
                        ),
                      ),
                      decoration: BoxDecoration(
                        color: _getColorByEvent(item.toString()),
                        borderRadius: BorderRadius.circular(10),
                      ),
                    ),
                  ),
                )
                .toList(),
          ),
        );
      },
    );
  }

만약 토요일, 일요일의 색상을 각 각 지정해주고 싶다면 CalendarBuilders 속성에 다음과 같이 추가해준다.

TableCalendar(
            calendarBuilders: CalendarBuilders(
              defaultBuilder: (context, day, focusedDay) {
                final text = day.day.toString();
                return _weekendText(text, day);
              },
              dowBuilder: (context, day) {
                final text = DateFormat.E('ko-KR').format(day);
                return _weekendText(text, day);
              },
              
              
 ...
 
   Widget _weekendText(String text, DateTime day) {
    return Center(
      child: Text(
        text,
        style: TextStyle(
            color: day.weekday == DateTime.sunday
                ? Colors.red
                : day.weekday == DateTime.saturday
                    ? Colors.blue
                    : null),
      ),
    );
  }

 

 

 

 

Reference

https://stackoverflow.com/questions/68166479/adding-events-to-table-calendar

 

Adding events to table_calendar

I am very new to flutter/Dart programming and am confused by the code below. I didn't write the code but I would like to use it to display event markers on the calendar grid of the table_calendar p...

stackoverflow.com

 

https://velog.io/@jun7332568/%ED%94%8C%EB%9F%AC%ED%84%B0flutter-%EB%8B%AC%EB%A0%A5-Event-%EA%B5%AC%ED%98%84%ED%95%B4%EB%B3%B4%EA%B8%B0-Tablecalendar-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC

 

[플러터/flutter] 달력 Event 구현해보기 ( Table_calendar 라이브러리)

이번엔 event를 설정하는 방법을 알아보자. 이를 통하면 특정 날짜(DateTime)에 어떠한 형태의 객체든 귀속시켜서 관리할 수 있어서 매우 편하다.이게 무슨 말이냐? event에 넣을 객체를 우리가 직접

velog.io

 

https://stackoverflow.com/questions/53908405/how-to-add-a-new-pair-to-map-in-dart

 

How to add a new pair to Map in Dart?

I caught the following errors when adding a new pair to a Map. Variables must be declared using the keywords const, final, var, or a type name Expected to find; the name someMap is already defined...

stackoverflow.com

 

https://stackoverflow.com/questions/68751527/flutter-futuremapdatetime-dynamic-cant-be-assigned-to-mapdatetime-dynam

 

Flutter: Future<Map<DateTime, dynamic>> can't be assigned to Map<DateTime, dynamic>

I have a function that gets data from Firestore and adds it to a Map. And when i try to use it i got this error The argument type 'Future<Map<DateTime, dynamic>>' can't be assigned to the

stackoverflow.com

 

 

 

 

 
728x90