【Flutter】table_calendarの使い方、デザイン例
Flutterアプリでカレンダーを作成するパッケージtable_calendar
の使い方、デザインの例を紹介する。
本記事で使用しているバージョンは以下。
table_calendar: 3.1.0
インストール
pubspec.yaml
を更新する。
dependencies:
table_calendar: # インストールするバージョン
基本の使い方
基本のTableCalendar
デフォルトでは月単位のカレンダーが表示される。
この状態でできることは表示月の切り替えのみ。左右スワイプまたはアローマークのタップで表示月を切り替えできる。
日付をタップしてもマークの位置は変わらない。また「2 weeks」ボタンをタップしてもカレンダーが2週間ごと表示にはならない。
import 'package:flutter/material.dart';
import 'package:table_calendar/table_calendar.dart';
class CalendarPage extends StatelessWidget {
const CalendarPage({super.key});
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
// カレンダーを作成
child: TableCalendar(
// カレンダーの開始日
firstDay: DateTime.utc(2000, 1, 1),
// カレンダーの終了日
lastDay: DateTime.utc(2030, 12, 31),
// この日付を含む範囲が表示される
focusedDay: DateTime.now(),
),
),
);
}
}
機能の追加する
プロパティを設定することでTableCalendar
に様々な機能を追加することができる。
日付選択
日付をタップして選択した状態
デフォルトでは日付をタップしてもカーソル位置は変わらない。
カーソル位置はcurrentDay
で指定可能。その際onDaySelected
に日付をタップした際のハンドラを登録する必要がある。
またウィジェットはStatefulWidget
に変更するなど状態を可変にする必要がある。
class SampleCalendar extends StatefulWidget {
const SampleCalendar({super.key});
State<SampleCalendar> createState() => _SampleCalendarPageState();
}
class _SampleCalendarPageState extends State<SampleCalendar> {
// カレンダーが表示される日付
DateTime _focusedDay = DateTime.now();
// カレンダー上でマークが表示される日付
DateTime _currentDay = DateTime.now();
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: TableCalendar(
firstDay: DateTime.utc(2000, 1, 1),
lastDay: DateTime.utc(2030, 12, 31),
focusedDay: _focusedDay,
currentDay: _currentDay, // マークが表示される日付
onDaySelected: ((selectedDay, focusedDay) {
setState(() {
_currentDay = selectedDay; // タップした際にマーク位置を更新
_focusedDay = selectedDay; // タップした際にカレンダーの表示位置を更新
});
}),
),
),
);
}
}
特定の日付だけ選択可能にする
特定の日付だけを選択可能にしたい場合、enabledDayPredicate
を設定する。
enabledDayPredicate
に対してtrue
を返却する日付だけ選択可能になる。
例えば2024年4月1日のみを選択可能にする場合、以下のように記載する。
enabledDayPredicate: (day) {
return day == DateTime.utc(2024, 4, 1);
},
またこの機能を用いると、日にちが偶数の日付のみ選択可能にもできたりする。
偶数の日付だけを選択可能
final enableDays = List.generate(3,
(index) => (index + 1) % 2 == 0 ? DateTime.utc(2024, 3, index + 1) : null);
// ...
TableCalendar(
enabledDayPredicate: (day) {
return enableDays.contains(day);
},
// ...
)
複数日付を選択
デフォルトでは日付は一つしか選択できない。
複数日付を選択する場合はselectedDayPredicate
を指定する。
日付を複数選択した状態
class SampleCalendar extends StatefulWidget {
const SampleCalendar({super.key});
State<SampleCalendar> createState() => _SampleCalendarPageState();
}
class _SampleCalendarPageState extends State<SampleCalendar> {
DateTime _focusedDay = DateTime.now();
DateTime _currentDay = DateTime.now();
// 選択した日付一覧
final selectedDays = [];
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: TableCalendar(
selectedDayPredicate: (day) => selectedDays.contains(day),
firstDay: DateTime.utc(2000, 1, 1),
lastDay: DateTime.utc(2030, 12, 31),
focusedDay: _focusedDay,
currentDay: _currentDay,
onDaySelected: ((selectedDay, focusedDay) {
setState(() {
_currentDay = selectedDay;
_focusedDay = selectedDay;
// タップした日付を選択した日付一覧に追加・削除
if (selectedDays.contains(selectedDay)) {
selectedDays.remove(selectedDay);
} else {
selectedDays.add(selectedDay);
}
});
}),
),
),
);
}
}
範囲選択
範囲を選択した状態
デフォルトでは日付を一つしか選択できないが、rangeSelectionMode
でモード指定を行うことで、範囲選択することが可能になる。
rangeSelectionMode
に渡すプロパティとその説明は以下。
プロパティ名 | 説明 |
---|---|
RangeSelectionMode.disabled | (デフォルト)常時範囲選択は無効 |
RangeSelectionMode.toggledOff | 現在範囲選択は無効で、日付を長押しすることで範囲選択を有効化 |
RangeSelectionMode.toggledOn | 現在範囲選択は無効で、日付を長押しすることで範囲選択を無効化 |
RangeSelectionMode.enforced | 常時範囲選択は有効 |
class CalendarPage extends StatefulWidget {
const CalendarPage({super.key});
State<CalendarPage> createState() => _CalendarPageState();
}
class _CalendarPageState extends State<CalendarPage> {
DateTime _focusedDay = DateTime.now();
// 範囲の開始日
DateTime? _rangeStartDay = DateTime.now();
// 範囲の終了日
DateTime? _rangeEndDay = DateTime.now();
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child:
TableCalendar(
firstDay: DateTime.utc(2000, 1, 1),
lastDay: DateTime.utc(2030, 12, 31),
focusedDay: _focusedDay,
// 範囲が開始する日付
rangeStartDay: _rangeStartDay,
// 範囲が終了する日付
rangeEndDay: _rangeEndDay,
// モードを指定して範囲を選択可能に
rangeSelectionMode: RangeSelectionMode.enforced,
// 日付をタップした際のイベント
onRangeSelected: (start, end, focusedDay) {
setState(() {
// 開始日を更新
_rangeStartDay = start;
// 終了日を更新
_rangeEndDay = end;
// 表示する日付を更新
_focusedDay = focusedDay;
});
},
),
),
);
}
}
フォーマット(1ヶ月表示、2週間表示、1週間表)切り替え
1ヶ月表示
2週間表示
1週間表示
onFormatChanged
を定義することでカレンダーのフォーマットを変更する機能を実装できる。
画面上でフォーマットを変更するには、画像に写っているカレンダーのヘッダーにあるボタンをタップする。
class CalendarPage extends StatefulWidget {
const CalendarPage({super.key});
State<CalendarPage> createState() => _CalendarPageState();
}
class _CalendarPageState extends State<CalendarPage> {
DateTime _focusedDay = DateTime.now();
// 選択中のフォーマット
CalendarFormat _calendarFormat = CalendarFormat.month;
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: TableCalendar(
locale: 'ja_JP',
firstDay: DateTime.utc(2000, 1, 1),
lastDay: DateTime.utc(2030, 12, 31),
focusedDay: _focusedDay,
// 選択中のフォーマット
calendarFormat: _calendarFormat,
// フォーマット変更ハンドラ
onFormatChanged: (format) => setState(() {
_calendarFormat = format;
}),
),
),
);
}
}
スワイプ操作の制御
TableCalendarは以下のスワイプ操作をすることができる。
- 水平方向(左右)スワイプ:カレンダー範囲切り替え
- 垂直方向(上下)スワイプ:カレンダーフォーマット切り替え
availableGestures
を指定することで上記のスワイプ操作を制御できる。
プロパティ | 説明 |
---|---|
AvailableGestures.all | 全てのジェスチャーが有効。 |
AvailableGestures.none | すべてのジェスチャーを無効化。 |
AvailableGestures.verticalSwipe | 垂直スワイプが有効。 |
AvailableGestures.horizontalSwipe | 水平スワイプが有効。 |
ロケール対応
カレンダーに表示される曜日や月の表記はデフォルト時英語。
locale
を指定することで、これらの表示をロケール対応できる。
ロケール先を日本に指定した場合
ロケール対応するにはintl
パッケージをインストールする。
dependencies:
flutter:
sdk: flutter
table_calendar: 3.1.0
intl:
runApp()
の前にinitializeDateFormatting()
を行う必要がある。
import 'package:intl/date_symbol_data_local.dart';
void main() {
initializeDateFormatting().then((_) => runApp(MyApp()));
}
最後にlocale
プロパティにロケールを指定する。
TableCalendar(
// ロケールを日本に指定
locale: "ja_JP",
//...
)
デザイン例
プロパティを設定することでデザインをカスタムすることができる。
ヘッダーを非表示にする
表示状態
非表示状態
デフォルトではヘッダーが表示されている。headerVisible
をfalse
に指定することで、ヘッダーを非表示にできる。
曜日を非表示にする
表示状態
非表示状態
デフォルトでは曜日が表示されている。daysOfWeekVisible
をfalse
に指定することで、曜日を非表示にできる。
開始曜日を変更する
日曜日開始
水曜日開始
デフォルトでは週は日曜日から始まる。startingDayOfWeek
の値を指定することで、週の開始曜日を変更できる。
// 水曜日に指定
startingDayOfWeek: StartingDayOfWeek.wednesday,
日付の表示を変更する
デフォルトでは日付は日にちが表示される。
日付のデザインを変更するにはcalendarBuilders
を設定する。
calendarBuilders: CalendarBuilders(
// デザインを指定
)
CalendarBuilders()
には複数のプロパティがあり、それぞれ次の部分を設定できる。
ビルダー | 説明 |
---|---|
prioritizedBuilder | 有効日、今日、選択日、範囲の始点/終点、外部の日付、非アクティブ日付などを優先的にビルドする。 |
todayBuilder | 今日の日付にカスタムウィジェットをビルドする。 |
selectedBuilder | 選択された日付にカスタムウィジェットをビルドする。 |
rangeStartBuilder | 範囲の始点にカスタムウィジェットをビルドする。 |
rangeEndBuilder | 範囲の終点にカスタムウィジェットをビルドする。 |
withinRangeBuilder | 範囲内の日付にカスタムウィジェットをビルドする。 |
outsideBuilder | 範囲外の日付にカスタムウィジェットをビルドする。 |
disabledBuilder | 無効な日付にカスタムウィジェットをビルドする。 |
holidayBuilder | 祝日にカスタムウィジェットをビルドする。 |
defaultBuilder | デフォルトのビルダーで、通常の日付にカスタムウィジェットをビルドする。 |
rangeHighlightBuilder | 範囲のハイライトにカスタムウィジェットをビルドする。 |
singleMarkerBuilder | 単一日に対するマーカーをビルドする。 |
markerBuilder | 日付ごとの複数のマーカーをビルドする。 |
dowBuilder | 曜日(日曜日から土曜日まで)にカスタムウィジェットをビルドする。 |
headerTitleBuilder | カレンダーのヘッダーに表示されるタイトル(月と年)にカスタムウィジェットをビルドする。 |
weekNumberBuilder | 週番号にカスタムウィジェットをビルドする。 |
例えば今日の日付の表示を変更するにはtodayBuilder
を指定する。
TableCalendar(
firstDay: DateTime.utc(2000, 1, 1),
lastDay: DateTime.utc(2030, 12, 31),
focusedDay: DateTime.now(),
calendarBuilders: CalendarBuilders(
// 今日の日付のデザインを指定
todayBuilder: (context, todayDay, focusDay) =>
const Center(child: Icon(Icons.star))),
),
日にちは第二引数から参照できる。
TableCalendar(
firstDay: DateTime.utc(2000, 1, 1),
lastDay: DateTime.utc(2030, 12, 31),
focusedDay: DateTime.now(),
calendarBuilders: CalendarBuilders(
// 今日の日付のデザインを指定
todayBuilder: (context, todayDay, focusDay) => Center(
child: Container(
height: double.infinity,
width: double.infinity,
color: Colors.blue,
child: Center(
child: Text(
todayDay.day.toString(),
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w900,
fontSize: 24),
))))),
),
サイズを小さくする
TableCalendar
を小さく表示するには少しコツがいる。
今回は下画像の様にカレンダーのサイズを、画面サイズの半分に小さくしてみる。
小さく表示したTableCalendar
ヘッダーは非表示。また今日の日付は青いマークが表示される様に指定した。
SizedBox(
// 幅を画面サイズの半分
width: MediaQuery.of(context).size.width * 0.5,
child: TableCalendar(
firstDay: DateTime.utc(2000, 1, 1),
lastDay: DateTime.utc(2030, 12, 31),
focusedDay: _focusedDay,
// 列の高さ30px(デフォルトは52px)
rowHeight: 30,
// ヘッダーを非表示
headerVisible: false,
// カレンダーの各要素のスタイルを指定
calendarBuilders: CalendarBuilders(
// 今日の日付のスタイル
todayBuilder: (context, day, focusedDay) {
return Container(
width: 20,
height: 20,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
color: Colors.blue),
child: Center(
child: Text(
day.day.toString(),
style: TextStyle(
color: Colors.white, fontWeight: FontWeight.w600),
),
),
);
},
// 今日以外の範囲内日付のスタイル
defaultBuilder: (context, day, focusedDay) {
return Text(day.day.toString());
},
// 範囲外日付のスタイル
outsideBuilder: (context, day, focusedDay) {
return Text(
day.day.toString(),
// 範囲外なので文字色グレー
style: const TextStyle(color: Colors.grey),
);
}),
),
),
単にSizedBox
でラップするだけではエラーになるので、各日付セルの高さや日付文字のサイズを調整する必要がある。
プロパティ
TableCalendar
ウィジェットのプロパティ一覧。
プロパティ名 | 役割 |
---|---|
focusedDay | (必須)フォーカスされている日付、カレンダーはこの日付が含まれる月や週を表示する |
firstDay | (必須)カレンダーの最初の日 |
lastDay | (必須)カレンダーの最後の日 |
currentDay | 現在選択中の日付、デフォルトでは丸いマークがついている |
locale | カレンダーが対応するロケールを指定 |
rangeStartDay | 範囲の開始日 |
rangeEndDay | 範囲の終了日 |
weekendDays | 週末の日、デフォルトは土日 |
calendarFormat | カレンダーの表示フォーマット |
availableCalendarFormats | 利用可能なカレンダーフォーマット |
headerVisible | ヘッダーの表示 |
daysOfWeekVisible | 曜日の表示 |
pageJumpingEnabled | ページジャンプの有効化 |
pageAnimationEnabled | 範囲外日付タップ時の、ページ遷移アニメーション有効化 |
sixWeekMonthsEnforced |
true で必ず6週間表示 |
shouldFillViewport |
true で可能な限りカレンダーが広がる |
weekNumbersVisible |
true で週の番号を表示 |
rowHeight | ヘッダーを除く日付の行の高さ |
daysOfWeekHeight | 曜日行の高さ |
formatAnimationDuration | フォーマット切り替えアニメーションの時間 |
formatAnimationCurve | フォーマット切り替えアニメーションのCurveを指定 |
pageAnimationDuration | カレンダー範囲切り替えのアニメーション時間 |
pageAnimationCurve | カレンダー範囲切り替えのアニメーションのCurveを指定 |
startingDayOfWeek | 週の始まりの曜日を指定 |
dayHitTestBehavior | 日付をタップした際のヒットテストの挙動を指定 |
availableGestures | 利用可能なジェスチャー |
simpleSwipeConfig | 左右スワイプの挙動を制御する |
headerStyle | ヘッダーのスタイルを指定 |
daysOfWeekStyle | 曜日のスタイルを指定 |
calendarStyle | カレンダーのスタイルを指定 |
calendarBuilders | カレンダーのビルダー |
rangeSelectionMode | 範囲の選択モード |
eventLoader | イベントの読み込み関数 |
enabledDayPredicate | 有効な日のプレディケート |
selectedDayPredicate | 選択された日のプレディケート |
holidayPredicate | 休日のプレディケート |
onRangeSelected | 範囲が選択されたときのコールバック |
onDaySelected | 日が選択されたときのコールバック |
onDayLongPressed | 日が長押しされたときのコールバック |
onDisabledDayTapped | 無効な日がタップされたときのコールバック |
onDisabledDayLongPressed | 無効な日が長押しされたときのコールバック |
onHeaderTapped | ヘッダーがタップされたときのコールバック |
onHeaderLongPressed | ヘッダーが長押しされたときのコールバック |
onPageChanged | ページが変更されたときのコールバック |
onFormatChanged | フォーマットが変更されたときのコールバック |
onCalendarCreated | カレンダーが作成されたときのコールバック |
Discussion