[Flutter/dart]Make your own calendar
Overview
There are many opportunities to use the calendar with a smartphone app. There are many libraries that can use calendars, but often you can't do what you want.
Especially, it seems that it is difficult to display other widgets along with the date.
In such a case, let's make a calendar by yourself.
Method
Get the dates included in the specified month
First, let's get the date included in a certain month. Start from the 1st and add one day at a time until the month changes. (Of course you can devide case such as January is until 31st, February is until 28th..., however it is quite troublesome considering the leap year)
List<List<DateTime>> _datesInMonth(int year, int month) {
List<List<DateTime>> _dates = [];
List<DateTime> _datesInWeek=[];
DateTime _date = DateTime(year, month, 1);
while (_date.month == month) { //1
_datesInWeek.add(_date);
if (_date.weekday==DateTime.saturday){
_dates.add(_datesInWeek);
_datesInWeek=[]; //2 Separated by Saturday
}
_date = _date.add(Duration(days: 1));
}
if (_datesInWeek.length>0){
_dates.add(_datesInWeek);
}
return _dates;
}
The above is a function that returns a list of "list of days in a week".
Start on the first day of the month and add more dates until the month changes (1).
After adding Saturday, the "Weekly List" is complete (2).
"Weekly list" to calendar widget
Then convert each "weekly list" to a calendar-style widget.
TableRow _weekWidget(List<DateTime> _dates){
List<Widget> widgets=[];
int startWeekDay=_dates[0].weekday;
bool _isFirstWeek =(_dates[0].day==1);
DateTime _additionalDate;
if (startWeekDay==DateTime.sunday){ //start from Sunday
widgets=_dates.map((DateTime d) =>
_dayWidget(d, _isFirstWeek)).toList();
_additionalDate=_dates[_dates.length-1].add(Duration(days: 1));
while(widgets.length<DateTime.daysPerWeek){
//At the end of the month, add the day of the next month until the
//length of the week becomes 7.
widgets.add(_dayWidget(_additionalDate, _isFirstWeek));
_additionalDate=_additionalDate.add(Duration(days: 1));
}
}
else{ //start from date other than Sunday(=the first week)
_additionalDate=_dates[0].subtract(Duration(days: 1));
List<DateTime> _additionalDates=[];
//previous month
while(_additionalDates.length<DateTime.daysPerWeek-_dates.length){
_additionalDates.add(_additionalDate);
_additionalDate=_additionalDate.subtract(Duration(days: 1));
}
int _len=_additionalDates.length;
for (int i=0; i<_len; i++){
widgets.add(_dayWidget(_additionalDates[_len-i-1],
_isFirstWeek));
}
widgets.addAll(_dates.map((DateTime d)
=>_dayWidget(d, _isFirstWeek)));
}
return TableRow(
children: widgets
);
}
We will make each day included in the weekly list a widget (_dayWidget details will be described later).
However, at the beginning and end of the month, the weekly list may contain less than 7 days, so add the previous or next month.
Date to widget
The method to make each day widget is as follows.
Widget _dayWidget(DateTime date, bool addWeekDayStr){
const double PAD_DEFAULT=5;
double pad_left= date.weekday==DateTime.sunday? 0: PAD_DEFAULT;
double pad_right=date.weekday==DateTime.saturday?0: PAD_DEFAULT;
List<Widget> _dayColumn=[];
DateTime _now=DateTime.now();
bool _isToday=((date.year==_now.year) && (date.month==_now.month)
&& (date.day==_now.day));
if (addWeekDayStr) {
_dayColumn.add(Text(WEEKDAYS[date.weekday])); //1 weekday
_dayColumn.add(SizedBox(height: 8,));
}
_dayColumn.addAll([
Container(
padding: const EdgeInsets.all(2),
alignment: Alignment.center,
decoration: _isToday? BoxDecoration(color: Colors.red ,shape:
BoxShape.circle): null, //2 make bacground red today
child: Text(
date.day.toString(),
style:
TextStyle(
fontSize: 16,
color: _isToday? Colors.white :dateColor(date),
//3 change color in Saturday and Sunday
),
),
),
SizedBox(height: 4,)
]);
//4 Add your desired widget
_dayColumn.add(Icon(Icons.brightness_1, color: Colors.green,));
return Container(
padding: EdgeInsets.only(left: pad_left, right: pad_right,
bottom: 5),
child: Column(
children: _dayColumn,
),
);
}
If that day is the first week of the month, addWeekDayStr will be true and the day of the week will be added above the date (1).
Today the date background is red (2).
The text color of the date should be black on weekdays, blue on Saturdays, and red on Sundays (3).
You can add your desired widget under the date (4).
The below is a helper method that returns the color of the date according to the day of the week
static Color dateColor(DateTime date){
switch(date.weekday){
case DateTime.sunday:
return Colors.red;
case DateTime.saturday:
return Colors.blue;
default:
return Colors.black;
}
}
Change the displayed month
The change of the displayed month is as follows.
void _changeRange(int direction){
_selectedDay = DateTime(_selectedDay.year, _selectedDay.month +
direction, 1);
setState(() {});
}
The entire code
Create a calendar as follows using the above.
Widget _calendarWidget(DateTime startDay){
int _year=startDay.year;
int _month=startDay.month;
List<List<DateTime>> _drawnDates;
const int MOVE_LEFT=-1;
const int MOVE_RIGHT=1;
_drawnDates=_datesInMonth(_selectedDay.year, _selectedDay.month);
return
Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
GestureDetector(
child: Container(
padding: const EdgeInsets.only(right: 16),
color: Colors.transparent,
child: Text(
"<",
style: TextStyle(
fontSize: 18
),
),
),
onTap: ()=>_changeRange(MOVE_LEFT),
),
Text(
"${_year}年${_month}月",
style: TextStyle(
fontSize: 18
),
),
GestureDetector(
child: Container(
padding: const EdgeInsets.only(left: 16),
color: Colors.transparent,
child: Text(
">",
style: TextStyle(
fontSize: 18
),
),
),
onTap: ()=>_changeRange(MOVE_RIGHT),
),
],
),
SizedBox(height: 16,),
Table(
children: _drawnDates.map((List<DateTime> _days)
=>_weekWidget(_days)).toList(),
),
]
);
}
You can create the following calendar.
Recent Posts
See AllWhat want to do I want to create an input form using TextField. For example, if the input content is a monetary amount, I would like to...
What want to do There is a variable that remain unchanged once the initial value is determined. However, it cannot be determined yet when...
What want to do As the title suggests. Place two widgets in one line on the screen One in the center of the screen and the other on the...
Comentarios