top of page

[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 All

[Flutter/Dart] Format string with TextField

What 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 display it in 3-digit delimiters with a ¥ prefix. Rather than ha

Let's do our best with our partner:​ ChatReminder

iphone6.5p2.png

It is an application that achieves goals in a chat format with partners.

google-play-badge.png
Download_on_the_App_Store_Badge_JP_RGB_blk_100317.png

Let's do our best with our partner:​ ChatReminder

納品:iPhone6.5①.png

It is an application that achieves goals in a chat format with partners.

google-play-badge.png
Download_on_the_App_Store_Badge_JP_RGB_blk_100317.png

Theme diary: Decide the theme and record for each genre

It is a diary application that allows you to post and record with themes and sub-themes for each genre.

google-play-badge.png
Download_on_the_App_Store_Badge_JP_RGB_blk_100317.png
bottom of page