Flutter
[Flutter] 플러터로 달력 만들기 (라이브러리 X) #1 달력 구현
병띠
2023. 11. 23. 17:27
안녕하세요.
오늘은 플러터로 라이브러리를 사용하지 않고 달력을 구현하려 합니다.
라이브러리를 사용하자니 커스텀 하기도 어렵고 원하는 디자인을 만들기도 힘들어 직접 구현해보겠습니다.
전체 코드: https://github.com/DevDotUng/Calender
GitHub - DevDotUng/Calender
Contribute to DevDotUng/Calender development by creating an account on GitHub.
github.com
구현 화면

전체 코드
더보기를 누르시면 코드를 보실 수 있습니다.
더보기
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: Home(),
);
}
}
class Home extends StatelessWidget {
const Home({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
body: SizedBox(
width: double.infinity,
height: double.infinity,
child: Center(
child: Calender(
width: 200,
height: 400,
saturdayTextColor: Colors.blue,
sundayTextColor: Colors.red,
todayColor: Colors.green,
),
),
),
);
}
}
class Calender extends StatefulWidget {
Calender({
Key? key,
required this.width,
required this.height,
this.saturdayTextColor = Colors.black,
this.sundayTextColor = Colors.red,
this.todayColor = Colors.blue,
}) : super(key: key);
double width;
double height;
Color? saturdayTextColor;
Color? sundayTextColor;
Color? todayColor;
@override
_CalenderState createState() => _CalenderState();
}
class _CalenderState extends State<Calender> {
var week = ["일", "월", "화", "수", "목", "금", "토"];
int year = 0;
int month = 0;
List days = [];
int weekNumber = 0;
setFirst(int setYear, int setMonth) {
year = setYear;
month = setMonth;
insertDays(year, month);
}
insertDays(int year, int month) {
days.clear();
weekNumber = ((DateTime(year, month, 1).weekday % 7 +
DateTime(year, month + 1, 0).day) /
7.0)
.ceil();
int lastDay = DateTime(year, month + 1, 0).day;
for (var i = 1; i <= lastDay; i++) {
days.add({
"year": year,
"month": month,
"day": i,
"inMonth": true,
"picked": false,
});
}
if (DateTime(year, month, 1).weekday != 7) {
var temp = [];
int prevLastDay = DateTime(year, month, 0).day;
for (var i = DateTime(year, month, 1).weekday - 1; i >= 0; i--) {
temp.add({
"year": year,
"month": month - 1,
"day": prevLastDay - i,
"inMonth": false,
"picked": false,
});
}
days = [...temp, ...days];
}
var temp = [];
for (var i = 1; i <= weekNumber * 7 - days.length; i++) {
temp.add({
"year": year,
"month": month + 1,
"day": i,
"inMonth": false,
"picked": false,
});
}
days = [...days, ...temp];
}
Color? getDayOfWeekTextColor(int index) {
if (index % 7 == 0) {
return widget.sundayTextColor;
} else if (index % 7 == 6) {
return widget.saturdayTextColor;
} else {
return Colors.black;
}
}
bool isToday(int year, int month, int day) {
DateTime now = DateTime.now();
return now.year == year && now.month == month && now.day == day;
}
@override
void initState() {
setFirst(DateTime.now().year, DateTime.now().month);
super.initState();
}
@override
Widget build(BuildContext context) {
return SizedBox(
width: widget.width,
height: widget.height,
child: Column(
children: [
Container(
padding: EdgeInsets.symmetric(horizontal: widget.width / 25),
height: widget.height / 14 + widget.width / 25,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
for (var i = 0; i < week.length; i++)
Text(
week[i],
style: TextStyle(
color: i == 0
? widget.sundayTextColor
: i == week.length - 1
? widget.saturdayTextColor
: Colors.grey,
fontWeight: FontWeight.w400,
fontSize: widget.width / 14,
height: (28 / 14),
),
),
],
),
),
Expanded(
child: SizedBox(
width: widget.width,
child: GridView.builder(
padding: const EdgeInsets.all(0),
physics: const NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 7,
crossAxisSpacing: 0,
mainAxisSpacing: 0,
mainAxisExtent: (widget.height * 13 / 14) / weekNumber,
),
itemCount: days.length,
itemBuilder: (context, index) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: widget.width / 7,
height: widget.width / 7,
decoration: isToday(days[index]["year"],
days[index]["month"], days[index]["day"])
? BoxDecoration(
color: widget.todayColor,
borderRadius:
BorderRadius.circular(widget.width / 14),
)
: null,
child: Center(
child: Text(
days[index]["day"].toString(),
style: TextStyle(
color: days[index]["inMonth"]
? getDayOfWeekTextColor(index)
: Colors.grey,
fontSize: widget.width / 14,
fontWeight: FontWeight.w400,
),
),
),
),
],
);
},
),
),
),
],
),
);
}
}
1. 이번 달은 몇주가 있는지 구하기
weekNumber = ((DateTime(year, month, 1).weekday % 7 +
DateTime(year, month + 1, 0).day) /
7.0)
.ceil();
2. 이번 달 채우기
int lastDay = DateTime(year, month + 1, 0).day;
for (var i = 1; i <= lastDay; i++) {
days.add({
"year": year,
"month": month,
"day": i,
"inMonth": true,
"picked": false,
});
}
3. 저번 달 채우기
if (DateTime(year, month, 1).weekday != 7) {
var temp = [];
int prevLastDay = DateTime(year, month, 0).day;
for (var i = DateTime(year, month, 1).weekday - 1; i >= 0; i--) {
temp.add({
"year": year,
"month": month - 1,
"day": prevLastDay - i,
"inMonth": false,
"picked": false,
});
}
days = [...temp, ...days];
}
4. 다음 달 채우기
var temp = [];
for (var i = 1; i <= weekNumber * 7 - days.length; i++) {
temp.add({
"year": year,
"month": month + 1,
"day": i,
"inMonth": false,
"picked": false,
});
}
days = [...days, ...temp];
5. 채운 달력을 통해 위젯 만들기
@override
Widget build(BuildContext context) {
return SizedBox(
width: widget.width,
height: widget.height,
child: Column(
children: [
Container(
padding: EdgeInsets.symmetric(horizontal: widget.width / 25),
height: widget.height / 14 + widget.width / 25,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
for (var i = 0; i < week.length; i++)
Text(
week[i],
style: TextStyle(
color: i == 0
? widget.sundayTextColor
: i == week.length - 1
? widget.saturdayTextColor
: Colors.grey,
fontWeight: FontWeight.w400,
fontSize: widget.width / 14,
height: (28 / 14),
),
),
],
),
),
Expanded(
child: SizedBox(
width: widget.width,
child: GridView.builder(
padding: const EdgeInsets.all(0),
physics: const NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 7,
crossAxisSpacing: 0,
mainAxisSpacing: 0,
mainAxisExtent: (widget.height * 13 / 14) / weekNumber,
),
itemCount: days.length,
itemBuilder: (context, index) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: widget.width / 7,
height: widget.width / 7,
decoration: isToday(days[index]["year"],
days[index]["month"], days[index]["day"])
? BoxDecoration(
color: widget.todayColor,
borderRadius:
BorderRadius.circular(widget.width / 14),
)
: null,
child: Center(
child: Text(
days[index]["day"].toString(),
style: TextStyle(
color: days[index]["inMonth"]
? getDayOfWeekTextColor(index)
: Colors.grey,
fontSize: widget.width / 14,
fontWeight: FontWeight.w400,
),
),
),
),
],
);
},
),
),
),
],
),
);
}
6. 사용
Calender(
width: 200,
height: 400,
saturdayTextColor: Colors.blue,
sundayTextColor: Colors.red,
todayColor: Colors.green,
),
실제 사용할때는 위와 같이 사용하면 됩니다.
다음 포스팅에서는 전, 다음 달로 넘어가는 버튼과 현재 연월을 표시하는 기능을 만들어보겠습니다.
전체 코드: https://github.com/DevDotUng/Calender
GitHub - DevDotUng/Calender
Contribute to DevDotUng/Calender development by creating an account on GitHub.
github.com