Skip to content

Instantly share code, notes, and snippets.

@sagar2093
Created May 21, 2024 11:29
Show Gist options
  • Save sagar2093/268115b8531867da9c567b67d8e0d362 to your computer and use it in GitHub Desktop.
Save sagar2093/268115b8531867da9c567b67d8e0d362 to your computer and use it in GitHub Desktop.
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
void main() {
runApp(const CalendarGeneratorApp());
}
class CalendarGeneratorApp extends StatelessWidget {
const CalendarGeneratorApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
themeMode: ThemeMode.light,
theme: ThemeData(
brightness: Brightness.light,
useMaterial3: true,
scaffoldBackgroundColor: Colors.grey.shade200,
colorSchemeSeed: Colors.blueAccent),
home: const YearlyCalendarScreen(),
);
}
}
class YearlyCalendarScreen extends StatefulWidget {
const YearlyCalendarScreen({super.key});
@override
State<YearlyCalendarScreen> createState() => _YearlyCalendarScreenState();
}
class _YearlyCalendarScreenState extends State<YearlyCalendarScreen> {
final DateTime _today = DateTime.now();
final Map<int, List<DateTime>> _monthCellsMap = {};
final List<String> _holidays = [];
final Map<String, String> _holidaysMap = {};
@override
void initState() {
setHolidays();
List.generate(12, (index) {
generateMonthCells(DateTime(_today.year, index + 1, 1));
});
super.initState();
}
setHolidays() {
// US United States / Public holidays (2024)
_holidaysMap.addAll({
"2024-01-01": "New Year's Day",
"2024-01-15": "Martin Luther King Jr. Day",
"2024-02-19": "Presidents' Day",
"2024-05-27": "Memorial Day",
"2024-06-19": "Juneteenth National Independence Day",
"2024-07-04": "Independence Day",
"2024-09-02": "Labor Day",
"2024-10-14": "Columbus Day",
"2024-11-11": "Veterans Day",
"2024-11-28": "Thanksgiving Day",
"2024-12-25": "Christmas Day",
});
_holidays.addAll(_holidaysMap.keys.toList());
}
generateMonthCells(DateTime month) async {
List<DateTime> cells = [];
var totalDaysInMonth = DateUtils.getDaysInMonth(month.year, month.month);
var firstDayDt = DateTime(month.year, month.month, 1);
var previousMonthDt = firstDayDt.subtract(const Duration(days: 1));
var nextMonthDt = DateTime(month.year, month.month, totalDaysInMonth)
.add(const Duration(days: 1));
var firstDayOfWeek = firstDayDt.weekday;
var previousMonthDays =
DateUtils.getDaysInMonth(previousMonthDt.year, previousMonthDt.month);
// previous month days
var previousMonthCells = List.generate(
firstDayOfWeek - 1,
(index) => DateTime(previousMonthDt.year, previousMonthDt.month,
previousMonthDays - index));
cells.addAll(previousMonthCells.reversed);
// current month days
var currentMonthCells = List.generate(totalDaysInMonth,
(index) => DateTime(month.year, month.month, index + 1));
cells.addAll(currentMonthCells);
// next month days
var remainingCellCount = 35 - cells.length;
if (cells.length > 35) {
remainingCellCount = 42 - cells.length;
}
var nextMonthCells = List.generate(remainingCellCount,
(index) => DateTime(nextMonthDt.year, nextMonthDt.month, index + 1));
cells.addAll(nextMonthCells);
_monthCellsMap[month.month] = cells;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Custom Yearly Calendar"),
),
body: SafeArea(
child: SingleChildScrollView(
padding: const EdgeInsets.all(20),
child: Column(
children: [
...List.generate(12, (index) {
var calendarMonth = DateTime(_today.year, index + 1, 1);
var cells = _monthCellsMap[index + 1] ?? [];
return Card(
color: Colors.white,
margin: const EdgeInsets.only(bottom: 20),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
child: CalendarBlock(
calendarMonth,
cells,
_holidaysMap,
today: _today,
isJapanese: true,
header: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
calendarMonth.calendarTitle,
style: Theme.of(context).textTheme.titleLarge,
),
),
),
);
}),
],
),
),
),
);
}
}
class CalendarBlock extends StatelessWidget {
const CalendarBlock(
this.month,
this.cells,
this.holidaysMap, {
required this.today,
this.header,
this.isJapanese = true,
super.key,
});
final DateTime today;
final DateTime month;
final List<DateTime> cells;
final Map<String, String> holidaysMap;
final Widget? header;
final bool isJapanese;
@override
Widget build(BuildContext context) {
var holidays = holidaysMap.keys.toList();
return Container(
padding: const EdgeInsets.all(4),
decoration: BoxDecoration(
color: Colors.white, borderRadius: BorderRadius.circular(14)),
child: Column(
children: [
Align(alignment: Alignment.center, child: header ?? const SizedBox()),
const _WeekHeaders(),
GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 7,
childAspectRatio: 0.9,
mainAxisSpacing: 2,
crossAxisSpacing: 2,
),
itemCount: cells.length,
itemBuilder: (context, index) {
var item = cells[index];
var isToday = item.isToday;
var isSameMonth = item.month == month.month;
var isHoliday = holidays.contains(item.dateDash);
Color? bgColor;
Color? textColor;
if (item.isSaturday) {
textColor = Colors.blueAccent;
}
if (item.isSunday) {
textColor = Colors.red;
}
if (item.isToday && isHoliday) {
bgColor = Colors.red;
textColor = Colors.white;
} else if (isToday) {
bgColor = Colors.blueAccent;
textColor = Colors.white;
} else if (isHoliday) {
bgColor = const Color(0xFFFFEFEF);
textColor = Colors.red;
}
return Card(
color: bgColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
child: InkWell(
onTap: isHoliday
? () {
var holidayText = holidaysMap[item.dateDash] ??
"This day is Holiday";
showModalBottomSheet(
context: context,
backgroundColor: Colors.white,
builder: (_) {
return Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.min,
children: [
Text(
"${item.dateDashEEEE} (Holiday)",
style: Theme.of(context)
.textTheme
.headlineSmall,
),
vs10x2,
Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: const Color(0xFFffcc17)
.withOpacity(0.1),
borderRadius:
BorderRadius.circular(4)),
child: Text(holidayText),
),
vs10x2,
FilledButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text("Close"),
),
vs10x2,
],
));
});
}
: null,
borderRadius: BorderRadius.circular(10),
child: Opacity(
opacity: isSameMonth ? 1 : 0.3,
child: Stack(
children: [
Center(
child: Text(
item.day.toString(),
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
color: textColor),
)),
],
),
),
),
);
})
],
),
);
}
}
class _WeekHeaders extends StatelessWidget {
const _WeekHeaders({super.key});
@override
Widget build(BuildContext context) {
return SizedBox(
height: 28,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
.mapIndexed(
(i, item) => Expanded(
child: Text(
item,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w700,
color: _getTitleColor(i),
),
textAlign: TextAlign.center,
),
),
)
.toList()),
);
}
_getTitleColor(int index) {
if (index == 5) {
return Colors.blueAccent;
}
if (index == 6) {
return const Color(0xFFFF8181);
}
return const Color(0xAD1A1642);
}
}
extension DateTimeExt on DateTime {
toFormat([String? newPattern, String? locale]) {
return DateFormat(newPattern, locale).format(this);
}
String get dateDash => toFormat("yyyy-MM-dd");
String get dateDashTime => toFormat("yyyy-MM-dd HH:mm");
String get dateDashEEEE => toFormat("yyyy-MM-dd EEEE");
String get monthName => toFormat("MMMM");
String get calendarTitle => toFormat("MMMM yyyy");
bool get isSaturday => weekday == 6;
bool get isSunday => weekday == 7;
bool get isToday {
var today = DateTime.now();
return year == today.year && month == today.month && day == today.day;
}
}
const vs10x2 = SizedBox(height: 20);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment