6
8

More than 3 years have passed since last update.

Flutter2でカレンダーを自作する

Posted at

背景

  • カレンダーパッケージを使用せずに、カレンダーを作りたい
  • パッケージのものを使うと、融通が効かない
  • 始まりの曜日を変更したい

前提

  • Flutter 2.2.0 使用

使っているパッケージ

  • バージョンは状況に合わせて変更してください
  • flutter_hooks: ^0.17.0
  • hooks_riverpod: ^0.14.0+4
  • intl: ^0.17.0

こういうものができます

  • 横スクロールで月の変更
    • PageViewを使用しています
  • ドロップダウンメニューで曜日始まり変更
    • DropdownButtonを使用しています

calendar.gif

  • スタイルなどは適宜変更してください

カレンダーで必要なリストの作り方

  • 一つの月のカレンダーを作成するためには、それぞれのリストを作成し最後にガッちゃんこする感じになります

    • それぞれのリストでView側で使用したいオブジェクトに変換しています。
      • 前月
      • 今月
      • 来月
  • 前月


  // 前月の日を埋めるリストを作成する
  List<CalendarDate> prevPaddingDays(int offset) {
    final year = targetMonth.year;
    final month = targetMonth.month;
    final list = <CalendarDate>[];
    final firstDay = DateTime(year, month).weekday;
    final paddingDayCount = (firstDay + 7 - offset) % 7;
    final prevLastDate = DateTime(year, month, 0).day;
    for (var day = prevLastDate - paddingDayCount + 1; day < prevLastDate + 1; day++) {
      list.add(CalendarDate(
        year: Year(year),
        month: Month.values[DateTime(year, month - 1).month - 1],
        day: Day(day),
        dayOfWeek: DayOfWeek.values[(DateTime(year, month - 1, day).weekday % 7)],
        enabled: false,
      ));
    }
    return list;
  }
  • 今月
  // 今月の日にちリストを作成する
  List<CalendarDate> currentDays() {
    final year = targetMonth.year;
    final month = targetMonth.month;
    final list = <CalendarDate>[];
    final lastDate = DateTime(year, month + 1, 0);
    final currentDayCount = lastDate.day;
    for (var day = 1; day < currentDayCount + 1; day++) {
      list.add(CalendarDate(
        year: Year(year),
        month: Month.values[month - 1],
        day: Day(day),
        dayOfWeek: DayOfWeek.values[(DateTime(year, month, day).weekday % 7)],
        enabled: true,
      ));
    }
    return list;
  }
  • 来月
  // 来月の日を埋めるリストを作成する
  List<CalendarDate> nextPaddingDays(prevList, currentList) {
    final year = targetMonth.year;
    final month = targetMonth.month;
    final list = <CalendarDate>[];
    final paddingDayCount = (42 - (prevList.length + currentList.length)) % 7;
    for (var day = 1; day < paddingDayCount + 1; day++) {
      list.add(CalendarDate(
        year: Year(year),
        month: Month.values[DateTime(year, month + 1).month - 1],
        day: Day(day),
        dayOfWeek: DayOfWeek.values[(DateTime(year, month + 1, day).weekday % 7)],
        enabled: false,
      ));
    }
    return list;
  }
  • がっちゃんこして今月のカレンダーリストを作成
  /// カレンダーリストを作成する
  /// year: 年
  /// month: 月
  /// offset: 開始の日(日曜日スタートがoffset:0で、月曜日からにする場合はoffset:1にする)
  void createCalendarList({int offset = STARTING_ON_SUNDAY}) {
    final prevList = prevPaddingDays(offset);
    final currentList = currentDays();
    final nextList = nextPaddingDays(prevList, currentList);
    flatCalendarDate = [
      ...prevList,
      ...currentList,
      ...nextList,
    ];

    calendarMonthDate = to2Dim(flatCalendarDate, NUMBER_OF_ONE_WEEK);
    notifyListeners();
  }


  /// 2次元リストを作成する
  List<CalendarWeek> to2Dim(list, numOfElems) {
    if (list.isEmpty) {
      return [];
    }
    return [CalendarWeek(days: list.take(numOfElems).toList()), ...to2Dim(list.skip(numOfElems), numOfElems)];
  }

終わりに

  • 今回は、日付をタップしても何も起こりません
    • カレンダーリスト作成時にオブジェクトに、
      • 今日かどうかのフラグだったり、何かデータを追加したりするなどすると、日付ごとにイベントを行えると思います。
  • こちらに今回実装したカレンダーのリポジトリを置いておきます

参考

6
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
8