[flutter] table_calendar 커스터 마이징 구현하기

곽준영·2023년 1월 26일
post-thumbnail

version

  • table_calendar: ^3.0.9

pub.dev에서 주어진 table_calendar의 기본 코드 문서를 사진과 같이 커스터 마이징 해보았다.

utils.dart

import 'dart:collection';

import 'package:table_calendar/table_calendar.dart';

/// Example event class.
class Event {
  final String title;

  const Event(this.title);

  @override
  String toString() => title;
}

/// Example events.
///
/// Using a [LinkedHashMap] is highly recommended if you decide to use a map.
final kEvents = LinkedHashMap<DateTime, List<Event>>(
  equals: isSameDay,
  hashCode: getHashCode,
)..addAll(_kEventSource);

final _kEventSource = {
  for (var item in List.generate(50, (index) => index))
    DateTime.utc(kToday.year, kToday.month, item * 5): List.generate(
        item % 4 + 1, (index) => Event('Event $item | ${index + 1}'))
}..addAll({
    kToday: [
      Event('Today\'s Event 1'),
      Event('Today\'s Event 2'),
    ],
  });

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 - 10, kToday.month, kToday.day);
final kLastDay = DateTime(kToday.year + 10, kToday.month, kToday.day);

headerVisible: false로 헤더를 감추고 새로 헤더를 커스터마이징 해보았다.
그리고 markerBuilder를 이용해 이미지를 삽입하였다.

events_example.dart

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

import '../utils.dart';

class TableEventsExample extends StatefulWidget {
  const TableEventsExample({super.key});

  @override
  State<TableEventsExample> createState() => _TableEventsExampleState();
}

class _TableEventsExampleState extends State<TableEventsExample> {
  CalendarFormat _calendarFormat = CalendarFormat.month;
  DateTime? selectedDay;
  late PageController pageController;
  late final ValueNotifier<List<Event>> _selectedEvents;
  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() {
    super.initState();

    _selectedDay = _focusedDay;
    _selectedEvents = ValueNotifier(_getEventsForDay(_selectedDay!));
  }

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

  List<Event> _getEventsForDay(DateTime day) {
    // Implementation example
    return kEvents[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) {
    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('TableCalendar - Events'),
      ),
      body: Column(
        children: [
          Container(
            color: Colors.grey,
            padding: EdgeInsets.all(20),
            child: Column(
              children: [
                Container(
                  padding: EdgeInsets.all(16),
                  decoration: BoxDecoration(
                    borderRadius: BorderRadius.circular(13),
                    color: Colors.white,
                    boxShadow: const [
                      BoxShadow(
                        color: Color.fromRGBO(0, 0, 0, 0.1),
                        spreadRadius: 0,
                        offset: Offset(4, 1),
                        blurRadius: 8.0,
                      ),
                    ],
                  ),
                  child: Column(
                    children: [
                      Row(
                        children: [
                          Container(
                            child: Row(
                              children: [
                                SizedBox(
                                  width: 10,
                                ),
                                Text(
                                  DateFormat("MMMM yyyy").format(_focusedDay),
                                  style: TextStyle(
                                      fontWeight: FontWeight.w600,
                                      fontSize: 17),
                                ),
                                Container(width: 8),
                                Icon(
                                  Icons.arrow_forward_ios,
                                  size: 15,
                                  color: Color(0xffFF3B30),
                                ),
                              ],
                            ),
                          ),
                          Spacer(),
                          Container(
                              child: Row(
                            mainAxisAlignment: MainAxisAlignment.end,
                            children: [
                              IconButton(
                                  onPressed: () async {
                                    await pageController.previousPage(
                                      duration:
                                          const Duration(milliseconds: 300),
                                      curve: Curves.easeOut,
                                    );
                                    setState(() {
                                      if (kFirstDay.year != kToday.year - 10) {
                                        _focusedDay = _focusedDay
                                            .subtract(const Duration(days: 30));
                                      } else {
                                        null;
                                      }
                                    });
                                  },
                                  icon: Icon(
                                    Icons.arrow_back_ios,
                                    color: Color(0xffFF3B30),
                                  )),
                              IconButton(
                                  onPressed: () async {
                                    await pageController.nextPage(
                                      duration:
                                          const Duration(milliseconds: 300),
                                      curve: Curves.easeOut,
                                    );
                                    setState(() {
                                      if (kLastDay.year != kToday.year + 10) {
                                        _focusedDay = _focusedDay
                                            .add(const Duration(days: 30));
                                      } else {
                                        null;
                                      }
                                    });
                                  },
                                  icon: Icon(
                                    Icons.arrow_forward_ios,
                                    color: Color(0xffFF3B30),
                                  )),
                            ],
                          )),
                        ],
                      ),
                      Container(height: 15),
                      TableCalendar(
                        headerVisible: false,
                        calendarBuilders: CalendarBuilders(
                          dowBuilder: (context, day) {
                            switch (day.weekday) {
                              case 1:
                                return Center(
                                  child: Text(
                                    'MON',
                                    style: TextStyle(
                                        color: Color.fromRGBO(60, 60, 67, 0.3),
                                        fontWeight: FontWeight.w600),
                                  ),
                                );
                              case 2:
                                return Center(
                                  child: Text(
                                    'TUE',
                                    style: TextStyle(
                                        color: Color.fromRGBO(60, 60, 67, 0.3),
                                        fontWeight: FontWeight.w600),
                                  ),
                                );
                              case 3:
                                return Center(
                                  child: Text(
                                    'WED',
                                    style: TextStyle(
                                        color: Color.fromRGBO(60, 60, 67, 0.3),
                                        fontWeight: FontWeight.w600),
                                  ),
                                );
                              case 4:
                                return Center(
                                  child: Text(
                                    'THU',
                                    style: TextStyle(
                                        color: Color.fromRGBO(60, 60, 67, 0.3),
                                        fontWeight: FontWeight.w600),
                                  ),
                                );
                              case 5:
                                return Center(
                                  child: Text(
                                    'FRI',
                                    style: TextStyle(
                                        color: Color.fromRGBO(60, 60, 67, 0.3),
                                        fontWeight: FontWeight.w600),
                                  ),
                                );
                              case 6:
                                return Center(
                                  child: Text(
                                    'SAT',
                                    style: TextStyle(
                                        color: Color.fromRGBO(60, 60, 67, 0.3),
                                        fontWeight: FontWeight.w600),
                                  ),
                                );
                              case 7:
                                return Center(
                                  child: Text(
                                    'SUN',
                                    style: TextStyle(
                                        color: Color.fromRGBO(60, 60, 67, 0.3),
                                        fontWeight: FontWeight.w600),
                                  ),
                                );
                            }
                          },
                          markerBuilder: (context, day, events) {
                            if (events.length == 1)
                              return Align(
                                  alignment: Alignment(0.0, -0.5),
                                  child: Image.asset("assets/images/ic_x.png"));
                            if (events.length == 2)
                              return Align(
                                  alignment: Alignment(0.0, -0.5),
                                  child: Image.asset("assets/images/ic_1.png"));
                            if (events.length == 3)
                              return Align(
                                  alignment: Alignment(0.0, -0.5),
                                  child: Image.asset("assets/images/ic_2.png"));
                            if (events.length == 4)
                              return Align(
                                  alignment: Alignment(0.0, -0.5),
                                  child: Image.asset("assets/images/ic_3.png"));
                          },
                        ),
                        availableGestures: AvailableGestures.none,
                        firstDay: kFirstDay,
                        lastDay: kLastDay,
                        focusedDay: _focusedDay,
                        onCalendarCreated: (controller) =>
                            pageController = controller,
                        calendarFormat: _calendarFormat,
                        selectedDayPredicate: (day) {
                          // Use `selectedDayPredicate` to determine which day is currently selected.
                          // If this returns true, then `day` will be marked as selected.

                          // Using `isSameDay` is recommended to disregard
                          // the time-part of compared DateTime objects.
                          return isSameDay(_selectedDay, day);
                        },
                        onDaySelected: _onDaySelected,
                        onFormatChanged: (format) {
                          if (_calendarFormat != format) {
                            // Call `setState()` when updating calendar format
                            setState(() {
                              _calendarFormat = format;
                            });
                          }
                        },
                        onPageChanged: (focusedDay) {
                          // No need to call `setState()` here
                          _focusedDay = focusedDay;
                        },
                        rangeStartDay: _rangeStart,
                        rangeEndDay: _rangeEnd,
                        rangeSelectionMode: _rangeSelectionMode,
                        eventLoader: _getEventsForDay,
                        startingDayOfWeek: StartingDayOfWeek.sunday,
                        onRangeSelected: _onRangeSelected,
                        calendarStyle: CalendarStyle(
                            selectedDecoration: BoxDecoration(
                          color: Colors.red,
                          shape: BoxShape.circle,
                        )),
                      ),
                    ],
                  ),
                ),
              ],
            ),
          ),
          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]}'),
                        title: Text('${value[index]}'),
                      ),
                    );
                  },
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}
profile
I want to become a versatile freelancer programmer💻

0개의 댓글