
version
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]}'),
),
);
},
);
},
),
),
],
),
);
}
}