Data Presentation

Calendar

A calendar component for selecting and editing dates.

The calendar supports swipe gestures on mobile platforms, allowing users to navigate between pages by swiping left or right.

A FCalendarControl controls navigation, while a FDateSelectionControl controls the selection behavior (single date, multiple dates, or a range).

FCalendar, FDateSelectionController, and all FCalendarControllers return DateTimes in UTC timezone, truncated to the nearest day.

1@override
2Widget build(BuildContext _) => FCalendar.grid(
3 control: FGridCalendarControl(
4 start: DateTime.utc(2000),
5 end: DateTime.utc(2040),
6 ),
7 selectionControl: .managedSingle(),
8);
9

CLI

To generate a specific style for customization:

dart run forui style create calendar
dart run forui style create calendar-header
dart run forui style create calendar-day-picker
dart run forui style create calendar-month-picker
dart run forui style create calendar-year-picker

Usage

FCalendar.grid(...)

1FCalendar.grid(
2 style: const .delta(padding: .value(.zero)),
3 fixedWeeks: false,
4)

FCalendar.splitGrid(...)

1FCalendar.splitGrid(
2 style: const .delta(padding: .value(.zero)),
3 fixedWeeks: false,
4)

FCalendar.wheel(...)

1FCalendar.wheel(
2 style: const .delta(padding: .value(.zero)),
3 fixedWeeks: false,
4)

Examples

Fixed Weeks

By default, the day grid adjusts its height to each month's 4 — 6 weeks. Set fixedWeeks: true to keep the calendar's height constant.

1@override
2Widget build(BuildContext _) => FCalendar.grid(
3 control: FGridCalendarControl(
4 start: DateTime.utc(2000),
5 today: DateTime.utc(2026, 2, 15),
6 initial: DateTime.utc(2026, 2, 15),
7 end: DateTime.utc(2030),
8 ),
9 selectionControl: .managedSingle(),
10 fixedWeeks: true,
11);
12

Modes

Split Grid

1@override
2Widget build(BuildContext _) => FCalendar.splitGrid(
3 control: FGridSplitCalendarControl(
4 start: DateTime.utc(2000),
5 end: DateTime.utc(2040),
6 ),
7 selectionControl: .managedSingle(),
8);
9

Month-Year Wheel

1@override
2Widget build(BuildContext _) => FCalendar.wheel(
3 control: FWheelCalendarControl(
4 start: DateTime.utc(2000),
5 end: DateTime.utc(2040),
6 ),
7 selectionControl: .managedSingle(),
8);
9

Selection

Single Date

1@override
2Widget build(BuildContext _) => FCalendar.grid(
3 control: FGridCalendarControl(
4 start: DateTime.utc(2000),
5 end: DateTime.utc(2040),
6 ),
7 selectionControl: .managedSingle(),
8);
9

Multiple Dates

1@override
2Widget build(BuildContext _) => FCalendar.grid(
3 control: FGridCalendarControl(
4 start: DateTime.utc(2000),
5 today: DateTime.utc(2026, 7, 15),
6 end: DateTime.utc(2030),
7 ),
8 selectionControl: .managedMulti(
9 initial: {DateTime.utc(2026, 7, 17), DateTime.utc(2026, 7, 20)},
10 ),
11);
12

Range Selection

1@override
2Widget build(BuildContext _) => FCalendar.grid(
3 control: FGridCalendarControl(
4 start: DateTime.utc(2000),
5 today: DateTime.utc(2026, 7, 15),
6 end: DateTime.utc(2030),
7 ),
8 selectionControl: .managedRange(
9 initial: (DateTime.utc(2026, 7, 17), DateTime.utc(2026, 7, 20)),
10 ),
11);
12

Display Only

Pass selectionControl: .none() to render a read-only calendar that selects nothing.

1@override
2Widget build(BuildContext _) => FCalendar.grid(
3 control: FGridCalendarControl(
4 start: DateTime.utc(2000),
5 end: DateTime.utc(2030),
6 ),
7 selectionControl: .none(),
8);
9

Unselectable Dates

1@override
2Widget build(BuildContext _) => FCalendar.grid(
3 control: FGridCalendarControl(
4 selectable: (date) =>
5 !{DateTime.utc(2026, 7, 18), DateTime.utc(2026, 7, 19)}.contains(date),
6 start: DateTime.utc(2000),
7 today: DateTime.utc(2026, 7, 15),
8 end: DateTime.utc(2030),
9 ),
10 selectionControl: .managedMulti(
11 initial: {DateTime.utc(2026, 7, 17), DateTime.utc(2026, 7, 20)},
12 ),
13);
14
1@override
2Widget build(BuildContext _) => FCalendar.grid(
3 control: FGridCalendarControl(
4 start: DateTime.utc(2000),
5 end: DateTime.utc(2030),
6 ),
7 selectionControl: .managedSingle(),
8 footerBuilder: (context, controller, selectionController) => Padding(
9 padding: const .only(top: 8),
10 child: Wrap(
11 spacing: 6,
12 runSpacing: 6,
13 alignment: .center,
14 children: [
15 for (final (label, date) in [
16 ('Today', controller.today),
17 ('In a week', controller.today.add(const Duration(days: 7))),
18 ('In a month', controller.today.add(const Duration(days: 30))),
19 ])
20 FButton(
21 variant: .outline,
22 size: .sm,
23 mainAxisSize: .min,
24 onPress: () async {
25 selectionController.select(date);
26 await controller.animateToDayPicker(date);
27 },
28 child: Text(label),
29 ),
30 ],
31 ),
32 ),
33);
34

On this page