DevBoi

[Flutter] TableCalendar EventHandler 본문

[Mobile]/[Flutter]

[Flutter] TableCalendar EventHandler

HiSmith 2023. 9. 17. 16:49
반응형

플러터에서 달력에 대한 오픈소스를 사용하면서 이벤트 관련 내용을 개발했다.

개발 기능은 아래와 같다.

1. 일정 내용을 로드

2. 달력 컴포넌트에 일정을 표시

3. 일정을 클릭시 해당 일정에 대한 상세 내용 확인

4. 해당 상세 내용 클릭시, 팝업으로 그룹에 내용 표기

 

 

무튼 소스 내용은 아래와 같다.

1. 화면소스

import 'dart:collection';

import 'package:flutter/material.dart';
import 'package:flutter_naver_map/flutter_naver_map.dart';
import 'package:get/get.dart';
import 'package:goodshot/global-provider.dart';
import 'package:goodshot/util/const.dart';
import 'package:goodshot/view/group/http/group-api.dart';
import 'package:goodshot/view/group/http/group-join-api.dart';
import 'package:goodshot/view/group/popup/group-detail-popup.dart';
import 'package:goodshot/view/group/popup/group-schedule-detail-popup.dart';
import 'package:goodshot/view/schedule/dto/schedule.dart';
import 'package:goodshot/view/schedule/http/schedule-api.dart';

import 'package:goodshot/view/schedule/utils/schedule-util.dart';
import 'package:table_calendar/table_calendar.dart';


final GlobalProvider globalProvider = Get.find();
class TableCalendarScreen extends StatefulWidget {
  //Future.sync(() => getScheduleList(globalProvider.getUserId()).then((value) => eventList=value));

  @override
  _TableEventsExampleState createState() => _TableEventsExampleState();
}

class _TableEventsExampleState extends State<TableCalendarScreen> {

  Map<DateTime,List<Event>> eventList = Map();

  late  ValueNotifier<List<Event>> _selectedEvents;
  CalendarFormat _calendarFormat = CalendarFormat.month;

  RangeSelectionMode _rangeSelectionMode = RangeSelectionMode
      .toggledOff; // Can be toggled on/off by longpressing a date
  DateTime _focusedDay = DateTime.now();
  DateTime? _selectedDay;
  DateTime? _rangeStart;
  DateTime? _rangeEnd;

  @override
  void initState() {
    Future.sync(() => getScheduleList(globalProvider.getUserId()).then((value) => eventList = value)).then((value) =>{
      setState(() {
        eventList = value;
        _selectedDay = _focusedDay;
        _selectedEvents = ValueNotifier(_getEventsForDay(_selectedDay!));
      })

    }
    );
    _selectedDay = _focusedDay;
    _selectedEvents = ValueNotifier(_getEventsForDay(_selectedDay!));
    super.initState();
  }

  @override
  void dispose() {
    _selectedEvents.dispose();
    super.dispose();
  }

  List<Event> _getEventsForDay(DateTime day) {

    return eventList[DateTime(day.year,day.month,day.day)] ?? [];
  }

  List<Event> _getEventsForRange(DateTime start, DateTime end) {
    // Implementation example
    final days = daysInRange(start, end);
    return [
      for (final d in days) ..._getEventsForDay(d),
    ];
  }

  void _onDaySelected(DateTime selectedDay, DateTime focusedDay) {
    print('select');
    print(eventList);
    if (!isSameDay(_selectedDay, selectedDay)) {
      setState(() {
        _selectedDay = selectedDay;
        _focusedDay = focusedDay;
        _rangeStart = null; // Important to clean those
        _rangeEnd = null;
        _rangeSelectionMode = RangeSelectionMode.toggledOff;
      });

      _selectedEvents.value = _getEventsForDay(selectedDay);
    }
  }


  void _onRangeSelected(DateTime? start, DateTime? end, DateTime focusedDay) {

    setState(() {
      _selectedDay = null;
      _focusedDay = focusedDay;
      _rangeStart = start;
      _rangeEnd = end;
      _rangeSelectionMode = RangeSelectionMode.toggledOn;
    });

    // `start` or `end` could be null
    if (start != null && end != null) {
      _selectedEvents.value = _getEventsForRange(start, end);
    } else if (start != null) {
      _selectedEvents.value = _getEventsForDay(start);
    } else if (end != null) {
      _selectedEvents.value = _getEventsForDay(end);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('굿샷 모임 캘린더'),

      ),
      body: Column(
        children: [
          TableCalendar<Event>(
            firstDay: kFirstDay,
            lastDay: kLastDay,
            focusedDay: _focusedDay,
            selectedDayPredicate: (day) => isSameDay(_selectedDay, day),
            rangeStartDay: _rangeStart,
            rangeEndDay: _rangeEnd,
            calendarFormat: _calendarFormat,
            rangeSelectionMode: _rangeSelectionMode,
            eventLoader: _getEventsForDay,
            startingDayOfWeek: StartingDayOfWeek.monday,
            locale: "ko-KR",
            headerStyle: HeaderStyle(
              formatButtonVisible: false,
              titleCentered: true,

            ),
            calendarStyle: CalendarStyle(
              // Use `CalendarStyle` to customize the UI
              outsideDaysVisible: false,
            ),
            onDaySelected: _onDaySelected,
            onRangeSelected: _onRangeSelected,
            onFormatChanged: (format) {
              if (_calendarFormat != format) {
                setState(() {
                  _calendarFormat = format;
                });
              }
            },
            onPageChanged: (focusedDay) {
              _focusedDay = focusedDay;
            },
          ),
          const SizedBox(height: 8.0),
          Expanded(
            child: ValueListenableBuilder<List<Event>>(
              valueListenable: _selectedEvents,
              builder: (context, value, _) {
                return ListView.builder(
                  itemCount: value.length,
                  itemBuilder: (context, index) {
                    return Container(
                      margin: const EdgeInsets.symmetric(
                        horizontal: 12.0,
                        vertical: 4.0,
                      ),
                      decoration: BoxDecoration(
                        border: Border.all(),
                        borderRadius: BorderRadius.circular(12.0),
                      ),
                      child: ListTile(
                        onTap: () {
                          //print('${value[index].groupNo}');
                          getGroupDetail(value[index].groupNo).then(
                               (value)=>
                              {
                                showDialog(context: context,
                                    builder: (BuildContext context){
                                      return GetGroupScheduleDetailPopup(getGroupItem: value);
                                    })
                              });
                        },
                        title: Text('${value[index]}'),
                      ),
                    );
                  },
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}

 

테이블 위젯을 로드하고 위젯 리스트를 불러온다.

그리고 해당 위젯 리스트는

LinkedHashMap<DateTime,List<Event>>의 형태로 가져온다.

해당 DateTime으로 그룹화를 시킨다.

해당 내용을 지원하는 유틸이다

import 'dart:collection';

import 'package:get/get.dart';
import 'package:goodshot/global-provider.dart';
import 'package:goodshot/view/schedule/dto/schedule.dart';
import 'package:goodshot/view/schedule/http/schedule-api.dart';
import 'package:table_calendar/table_calendar.dart';



/// Example events.
///
/// Using a [LinkedHashMap] is highly recommended if you decide to use a map.
// final events = LinkedHashMap(
//   equals: isSameDay,
//
// )..addAll(eventSource(String userId));

//late  Map<DateTime,List<Event>> eventList = Map<DateTime,List<Event>>();



Map<DateTime,List<Event>> settingSchedule(String userId) {
  late Map<DateTime,List<Event>> eventList = Map();
  getScheduleList(userId).then((value)=>{
    eventList = value,
  });
  return eventList;
}

//Map<DateTime,List<Event>> eventList
LinkedHashMap<DateTime, List<Event>>  getEventStore(Map<DateTime,List<Event>> eventList) {
  LinkedHashMap<DateTime, List<Event>> kEvents = LinkedHashMap<DateTime, List<Event>>(
    equals: isSameDay,
    hashCode: getHashCode,
  )..addAll(eventList);
  return kEvents;

}


int getHashCode(DateTime key) {
  return key.day * 1000000 + key.month * 10000 + key.year;
}

/// Returns a list of [DateTime] objects from [first] to [last], inclusive.
List<DateTime> daysInRange(DateTime first, DateTime last) {
  final dayCount = last.difference(first).inDays + 1;
  return List.generate(
    dayCount,
        (index) => DateTime.utc(first.year, first.month, first.day + index),
  );
}

final kToday = DateTime.now();
final kFirstDay = DateTime(kToday.year, kToday.month - 3, kToday.day);
final kLastDay = DateTime(kToday.year, kToday.month + 3, kToday.day);

 

나는 연,월,일에 대해서만 해당 값을 리턴하기 때문에 해당값에 대한 것들을 세팅한다.

마지막으로 api 와 dto이다.

DateTime dateOnly(DateTime dateTime) {
  if (dateTime.isUtc) {
    return DateTime.utc(dateTime.year, dateTime.month, dateTime.day);
  } else {
    return DateTime(dateTime.year, dateTime.month, dateTime.day);
  }
}

Future<Map<DateTime, List<Event>>> getScheduleList(String userId) async{
  Iterable l;

  List<Event> eventList;
  var eventDayMap = <DateTime, List<Event>>{};

  var url = Uri.http(backendHost, 'get-schedule',{"userId":userId});
  await http.get(url).then((value) =>
  {
    l = json.decode(value.body),
    eventList =  List<Event>.from(l.map((model)=> Event.fromJson(model))),
    for (var event in eventList) {
      (eventDayMap[dateOnly(event.date)] ??= []).add(event),
    }
  });
  return  eventDayMap;
}
class Event {
  final String title;
  final DateTime date;
  final int groupNo;

  const Event({required this.date, required this.title,required this.groupNo});

  @override
  String toString() => title;

  factory Event.fromJson(dynamic json){
    DateTime time = DateTime.parse(json?['date']);
     Event event = Event(
      title: json?['title'],
      groupNo: json?['group_no'],
      date: DateTime(time.year,time.month,time.day)

    );
    return event;
  }
}

 

해당 dto와 api로 값을 세팅하고, 사용하게 된다.

무튼 해당 방식으로 진행이 되고 아래와 같은 결과를 얻게 된다.

 

소스를 그대로 가져다 쓰면 오류는 안날 것이다

무튼 해당 관련 모듈구현은 끝났다.

 

소스를 좀 정리하고 다시 올려야겠다.

반응형