본문 바로가기
Flutter & Dart

[Flutter]플러터 달력 만들기

by Sudarii 2024. 2. 20.
728x90

 

 

아래의 이미지와 같은 주차별 합계, 일자별 사용 금액을 포함한 달력을 만들 것이다.

table_calendar 라는 패키지가 있지만 해당 패키지는 주차별 합계를 지원하지 않는 것으로 알고 있다.

그래서 나는 직접 만든다..

 

1. 달력 컨트롤 생성

  var week = ["일", "월", "화", "수", "목", "금", "토"];

 

요일은 배열에 따로 담아두었다.

 

  insertDays(int year, int month) {
    days.clear();

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

    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,
        });
      }
      days = [...temp, ...days].obs;
    }

    var temp = [];
    for (var i = 1; i <= 42 - days.length; i++) {
      temp.add({
        "year": year,
        "month": month + 1,
        "day": i,
        "inMonth": false,
      });
    }

    days = [...days, ...temp].obs;
  }

 

년, 월, 일, 이번달 포함여부 체크하여 days에 포함 시켰다.

 

2. 달력 만들기

  Widget canlendar() {
    return SizedBox(
      width: width,
      child: Column(
        children: [
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceAround,
            children: [
              IconButton(
                onPressed: () {
                  controller.previousMonth();
                },
                icon: Icon(Icons.arrow_back_ios,
                    size: 15, color: AppColors().color191919),
              ),
              SizedBox(
                child: Center(
                  child: Obx(
                    () => Text(
                      '${controller.month}월',
                      style: AppTextStyle().blackBold20,
                    ),
                  ),
                ),
              ),
              IconButton(
                onPressed: () {
                  controller.nextMonth();
                },
                icon: Icon(Icons.arrow_forward_ios,
                    size: 15, color: AppColors().color191919),
              ),
            ],
          ),
          SizedBox(
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: [
                for (var i = 0; i < controller.week.length; i++)
                  Text(
                    controller.week[i],
                    style: TextStyle(
                      color: i == 0
                          ? Colors.red
                          : i == controller.week.length - 1
                              ? Colors.blue
                              : Colors.black,
                    ),
                    textAlign: TextAlign.center,
                  ),
              ],
            ),
          ),
          ..........
        ],
      ),
    );
  }

 

 

상단의  <  1월  >을 위해 IconButton을 이용하여 버튼을 양쪽에 만들어 주었다.

그리고 일, 토요일은 색상 변경 해주었다.

 

Obx(
    () => Container(
      width: width,
      child: Column(
        children: [
          calendarDay(0),
          calendarDay(1),
          calendarDay(2),
          calendarDay(3),
          calendarDay(4),
          calendarDay(5),
        ],
      ),
    ),
  )

 

calendarDay를 최대 6주차 까지 호출해주었다.

 

  Widget calendarDay(int num) {
    int iz = num * 7;
    int yz = (num + 1) * 7;
    RxBool dayCheck = false.obs;
    for (var i = iz; i < yz; i++) {
      if (controller.days[i]["inMonth"]) {
        if (!dayCheck.value) {
          dayCheck.value = true;
        }
      }
    }
    return dayCheck.value
        ? ........

 

calendarDay 는 아래의 달력의 1주씩 따로 만들기 위함이다.

6주를 호출하지만 만약 이번달이 5주만 있다면 그냥 넘어가기 위해서다.

for 를 이용하여  이번주에 이번달에 해당하는 일자가 있는지 체크한다.

 return dayCheck.value
        ? Column(
            children: [
              Container(
                padding: const EdgeInsets.only(right: 10),
                width: width,
                height: 20,
                color: AppColors().colorf7f6f6,
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.end,
                  children: [
                    Text(
                      controller.getWeekSumMoney(num, '0'),
                      style: AppTextStyle().colortitle8,
                    ),
                    controller.getWeekSumMoney(num, '1') != ''
                        ? Container(
                            padding: const EdgeInsets.only(left: 10),
                            child: Text(
                              controller.getWeekSumMoney(num, '1'),
                              style: AppTextStyle().black8,
                            ),
                          )
                        : Container(),
                  ],
                ),
              ),
              Row(
               ............
            ],
          )
        : Container();

 

이번주에 입금, 출금의 합계를 구하기 위하여 사용하였다.

getDaySumMoney는 이번 주에 gubn이 같은 것들의 합계를 보여준다.

  String getDaySumMoney(String day, String gubn) {
    int money = 0;
    for (int i = 0; i < accountMonthSearch.length; i++) {
      List<String> parts = accountMonthSearch[i].date.split('-');
      DateTime reserDate = DateTime(
        int.parse(parts[0]),
        int.parse(parts[1]),
        int.parse(parts[2]),
      );
      int year = int.parse(DateFormat('yyyy').format(isSelectedDay.value));
      int month = int.parse(DateFormat('MM').format(isSelectedDay.value));

      if (year == reserDate.year &&
          month == reserDate.month &&
          int.parse(day) == reserDate.day &&
          accountMonthSearch[i].gubn == gubn) {
        money = money + accountMonthSearch[i].money;
      }
    }

    // accountMonthSearch
    return money.toString().stringToNum() == '0'
        ? ''
        : gubn == '0'
            ? '+${money.toString().stringToNum()}'
            : '-${money.toString().stringToNum()}';
  }

 

 

Row(
        mainAxisAlignment: MainAxisAlignment.spaceAround,
        children: [
          for (var i = iz; i < yz; i++)
            controller.days[i]["inMonth"]
                ? Obx(
                    () => InkWell(
                      onTap: () async {
                        for (var j = 0;
                            j < controller.days.length;
                            j++) {
                          controller.days[j]["picked"].value = false;
                        }

                        controller.onDateSelected(
                            controller.days[i]["day"].toString());
                      },
                      child: Stack(
                        children: [
                          Container(
                            width: (width - 10) / 7,
                            height: 60,
                            padding: const EdgeInsets.only(top: 10),
                            child: Text(
                              controller.days[i]["day"].toString(),
                              style: controller.days[i]["day"] ==
                                      controller.day.value
                                  ? AppTextStyle().blackBold16
                                  : AppTextStyle().black14,
                              textAlign: TextAlign.center,
                            ),
                          ),
                          Positioned(
                            right: 1,
                            bottom: 5,
                            child: Column(
                              crossAxisAlignment:
                                  CrossAxisAlignment.end,
                              children: [
                                Text(
                                  controller.getDaySumMoney(
                                      controller.days[i]["day"]
                                          .toString(),
                                      '0'),
                                  style: AppTextStyle().colortitle8,
                                ),
                                Text(
                                  controller.getDaySumMoney(
                                      controller.days[i]["day"]
                                          .toString(),
                                      '1'),
                                  style: AppTextStyle().black8,
                                ),
                              ],
                            ),
                          )
                        ],
                      ),
                    ),
                  )
                : Container(
                    width: 30,
                    margin: const EdgeInsets.symmetric(
                        horizontal: 10, vertical: 20),
                  ),
        ],
      ),

 

이번주에 해당 하는 일자를 표기한다.

선택된 일자는 bold체를 추가해 주었고, 입금, 출금 내역을 stack을 이용하여 추가하였다.

 

 

해당 소스들을 모두 적용하면 아래 사진과 같이 만들어진다.

728x90