Introduction:
A multidate picker allows users to select multiple dates from a calendar widget, which is useful for scenarios where users need to choose multiple dates for scheduling events, booking appointments, etc. In Flutter, the calendar_date_picker2
package provides functionality to implement a multidate picker easily.
Content:
Step 1: Add Dependency
- Begin by adding the
calendar_date_picker2
package to yourpubspec.yaml
file. This ensures that your Flutter project can access the functionalities provided by the package. - Run the following command in your terminal to fetch the package:
flutter pub get
Step 2: Import Package
- Import the
calendar_date_picker2
package in the Dart file where you want to implement the multidate picker. This allows you to utilize the CalendarDatePicker widget provided by the package.
import 'package:flutter/material.dart';
import 'package:calendar_date_picker/calendar_date_picker.dart';
import 'package:flutter/material.dart';
import 'package:calendar_date_picker/calendar_date_picker.dart';
Step 3: Implement Multidate Picker
- Use the CalendarDatePicker widget to implement the multidate picker in your Flutter app. Customize the widget as needed, including parameters such as initial selected dates, date range, callback functions, etc.
List<DateTime> selectedDates = [];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Multidate Picker'),
),
body: CalendarDatePicker(
initialSelectedDates: selectedDates,
onDateChange: (date) {
setState(() {
if (selectedDates.contains(date)) {
selectedDates.remove(date);
} else {
selectedDates.add(date);
}
});
},
),
);
}
Step 4: Customize Multidate Picker
- Customize the appearance and behavior of the multidate picker according to your app’s design requirements. You can adjust parameters such as the calendar theme, selection color, date format, etc.
Step 5: Handle Selected Dates
- Implement any desired functionality to handle the selected dates, such as displaying them in a list, performing actions based on the selected dates, etc.
Step 6: Test the Implementation
- After implementing the multidate picker, run your Flutter application on a device or emulator to test the functionality. Select multiple dates from the calendar widget and verify that the selected dates are captured correctly.
Step 7: Run the Application
- Run the Flutter application using the following command in your terminal:
flutter run
Sample Code:
import 'package:calendar_date_picker2/calendar_date_picker2.dart';
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
final today = DateUtils.dateOnly(DateTime.now());
class Multidatepicker extends StatelessWidget {
const Multidatepicker({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
localizationsDelegates: GlobalMaterialLocalizations.delegates,
supportedLocales: const [
Locale('en', ''),
Locale('zh', ''),
Locale('he', ''),
Locale('es', ''),
Locale('ru', ''),
Locale('ko', ''),
Locale('hi', ''),
],
home: const MyHomePage(title: 'CalendarDatePicker2 Demo'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<DateTime?> _dialogCalendarPickerValue = [
DateTime(2021, 8, 10),
DateTime(2021, 8, 13),
];
List<DateTime?> _singleDatePickerValueWithDefaultValue = [
DateTime.now(),
];
List<DateTime?> _multiDatePickerValueWithDefaultValue = [
DateTime(today.year, today.month, 1),
DateTime(today.year, today.month, 5),
DateTime(today.year, today.month, 14),
DateTime(today.year, today.month, 17),
DateTime(today.year, today.month, 25),
];
List<DateTime?> _rangeDatePickerValueWithDefaultValue = [
DateTime(1999, 5, 6),
DateTime(1999, 5, 21),
];
List<DateTime?> _rangeDatePickerWithActionButtonsWithValue = [
DateTime.now(),
DateTime.now().add(const Duration(days: 5)),
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
centerTitle: true,
elevation: 1,
title: Text(
'Calendar MaltiDatePicker',
style: TextStyle(fontSize: 20, color: Colors.white),
),
backgroundColor: Colors.blue[200],
),
body: Center(
child: SizedBox(
width: 375,
child: ListView(
children: <Widget>[
_buildCalendarDialogButton(),
_buildDefaultSingleDatePickerWithValue(),
_buildDefaultMultiDatePickerWithValue(),
_buildDefaultRangeDatePickerWithValue(),
_buildCalendarWithActionButtons(),
],
),
),
),
);
}
String _getValueText(
CalendarDatePicker2Type datePickerType,
List<DateTime?> values,
) {
values = values.map((e) => e != null ? DateUtils.dateOnly(e) : null).toList();
var valueText = (values.isNotEmpty ? values[0] : null).toString().replaceAll('00:00:00.000', '');
if (datePickerType == CalendarDatePicker2Type.multi) {
valueText = values.isNotEmpty ? values.map((v) => v.toString().replaceAll('00:00:00.000', '')).join(', ') : 'null';
} else if (datePickerType == CalendarDatePicker2Type.range) {
if (values.isNotEmpty) {
final startDate = values[0].toString().replaceAll('00:00:00.000', '');
final endDate = values.length > 1 ? values[1].toString().replaceAll('00:00:00.000', '') : 'null';
valueText = '$startDate to $endDate';
} else {
return 'null';
}
}
return valueText;
}
_buildCalendarDialogButton() {
const dayTextStyle = TextStyle(color: Colors.black, fontWeight: FontWeight.w700);
final weekendTextStyle = TextStyle(color: Colors.grey[500], fontWeight: FontWeight.w600);
final anniversaryTextStyle = TextStyle(
color: Colors.red[400],
fontWeight: FontWeight.w700,
decoration: TextDecoration.underline,
);
final config = CalendarDatePicker2WithActionButtonsConfig(
dayTextStyle: dayTextStyle,
calendarType: CalendarDatePicker2Type.range,
selectedDayHighlightColor: Colors.purple[800],
closeDialogOnCancelTapped: true,
firstDayOfWeek: 1,
weekdayLabelTextStyle: const TextStyle(
color: Colors.black87,
fontWeight: FontWeight.bold,
),
controlsTextStyle: const TextStyle(
color: Colors.black,
fontSize: 15,
fontWeight: FontWeight.bold,
),
centerAlignModePicker: true,
customModePickerIcon: const SizedBox(),
selectedDayTextStyle: dayTextStyle.copyWith(color: Colors.white),
dayTextStylePredicate: ({required date}) {
TextStyle? textStyle;
if (date.weekday == DateTime.saturday || date.weekday == DateTime.sunday) {
textStyle = weekendTextStyle;
}
if (DateUtils.isSameDay(date, DateTime(2021, 1, 25))) {
textStyle = anniversaryTextStyle;
}
return textStyle;
},
dayBuilder: ({
required date,
textStyle,
decoration,
isSelected,
isDisabled,
isToday,
}) {
Widget? dayWidget;
if (date.day % 3 == 0 && date.day % 9 != 0) {
dayWidget = Container(
decoration: decoration,
child: Center(
child: Stack(
alignment: AlignmentDirectional.center,
children: [
Text(
MaterialLocalizations.of(context).formatDecimal(date.day),
style: textStyle,
),
Padding(
padding: const EdgeInsets.only(top: 27.5),
child: Container(
height: 4,
width: 4,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5),
color: isSelected == true ? Colors.white : Colors.grey[500],
),
),
),
],
),
),
);
}
return dayWidget;
},
yearBuilder: ({
required year,
decoration,
isCurrentYear,
isDisabled,
isSelected,
textStyle,
}) {
return Center(
child: Container(
decoration: decoration,
height: 36,
width: 72,
child: Center(
child: Semantics(
selected: isSelected,
button: true,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
year.toString(),
style: textStyle,
),
if (isCurrentYear == true)
Container(
padding: const EdgeInsets.all(5),
margin: const EdgeInsets.only(left: 5),
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Colors.redAccent,
),
),
],
),
),
),
),
);
},
);
return Padding(
padding: const EdgeInsets.all(15),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.black,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5.0),
),
),
onPressed: () async {
final values = await showCalendarDatePicker2Dialog(
context: context,
config: config,
dialogSize: const Size(325, 400),
borderRadius: BorderRadius.circular(15),
value: _dialogCalendarPickerValue,
dialogBackgroundColor: Colors.white,
);
if (values != null) {
// ignore: avoid_print
print(_getValueText(
config.calendarType,
values,
));
setState(() {
_dialogCalendarPickerValue = values;
});
}
},
child: const Text('Open Calendar Dialog'),
),
],
),
);
}
Widget _buildDefaultSingleDatePickerWithValue() {
final config = CalendarDatePicker2Config(
selectedDayHighlightColor: Colors.amber[900],
weekdayLabels: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
weekdayLabelTextStyle: const TextStyle(
color: Colors.black87,
fontWeight: FontWeight.bold,
),
firstDayOfWeek: 1,
controlsHeight: 50,
controlsTextStyle: const TextStyle(
color: Colors.black,
fontSize: 15,
fontWeight: FontWeight.bold,
),
dayTextStyle: const TextStyle(
color: Colors.amber,
fontWeight: FontWeight.bold,
),
disabledDayTextStyle: const TextStyle(
color: Colors.grey,
),
selectableDayPredicate: (day) => !day.difference(DateTime.now().subtract(const Duration(days: 3))).isNegative,
);
return Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: 10),
const Text('Single Date Picker (With default value)'),
CalendarDatePicker2(
config: config,
value: _singleDatePickerValueWithDefaultValue,
onValueChanged: (dates) => setState(() => _singleDatePickerValueWithDefaultValue = dates),
),
const SizedBox(height: 10),
Row(
mainAxisSize: MainAxisSize.min,
children: [
const Text('Selection(s): '),
const SizedBox(width: 10),
Text(
_getValueText(
config.calendarType,
_singleDatePickerValueWithDefaultValue,
),
),
],
),
const SizedBox(height: 25),
],
);
}
Widget _buildDefaultMultiDatePickerWithValue() {
final config = CalendarDatePicker2Config(
calendarType: CalendarDatePicker2Type.multi,
selectedDayHighlightColor: Colors.indigo,
);
return Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: 10),
const Text('Multi Date Picker (With default value)'),
CalendarDatePicker2(
config: config,
value: _multiDatePickerValueWithDefaultValue,
onValueChanged: (dates) => setState(() => _multiDatePickerValueWithDefaultValue = dates),
),
const SizedBox(height: 10),
Wrap(
children: [
const Text('Selection(s): '),
const SizedBox(width: 10),
Text(
_getValueText(
config.calendarType,
_multiDatePickerValueWithDefaultValue,
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
softWrap: false,
),
],
),
const SizedBox(height: 25),
],
);
}
Widget _buildDefaultRangeDatePickerWithValue() {
final config = CalendarDatePicker2Config(
calendarType: CalendarDatePicker2Type.range,
selectedDayHighlightColor: Colors.teal[800],
weekdayLabelTextStyle: const TextStyle(
color: Colors.black87,
fontWeight: FontWeight.bold,
),
controlsTextStyle: const TextStyle(
color: Colors.black,
fontSize: 15,
fontWeight: FontWeight.bold,
),
);
return Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: 10),
const Text('Range Date Picker (With default value)'),
CalendarDatePicker2(
config: config,
value: _rangeDatePickerValueWithDefaultValue,
onValueChanged: (dates) => setState(() => _rangeDatePickerValueWithDefaultValue = dates),
),
const SizedBox(height: 10),
Row(
mainAxisSize: MainAxisSize.min,
children: [
const Text('Selection(s): '),
const SizedBox(width: 10),
Text(
_getValueText(
config.calendarType,
_rangeDatePickerValueWithDefaultValue,
),
),
],
),
const SizedBox(height: 25),
],
);
}
Widget _buildCalendarWithActionButtons() {
final config = CalendarDatePicker2WithActionButtonsConfig(
calendarType: CalendarDatePicker2Type.range,
disableModePicker: true,
);
return Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: 10),
const Text('Date Picker With Action Buttons'),
CalendarDatePicker2WithActionButtons(
config: config,
value: _rangeDatePickerWithActionButtonsWithValue,
onValueChanged: (dates) => setState(() => _rangeDatePickerWithActionButtonsWithValue = dates),
),
const SizedBox(height: 10),
Row(
mainAxisSize: MainAxisSize.min,
children: [
const Text('Selection(s): '),
const SizedBox(width: 10),
Flexible(
child: Text(
_getValueText(
config.calendarType,
_rangeDatePickerWithActionButtonsWithValue,
),
),
),
],
),
const SizedBox(height: 25),
],
);
}
}
Output:
Conclusion:
Integrating a multidate picker into your Flutter app using the calendar_date_picker2
package allows users to conveniently select multiple dates from a calendar widget. By following the steps outlined in this guide and customizing the appearance and behavior of the multidate picker as needed, you can enhance the scheduling and appointment booking experience for your app’s users. Experiment with different configurations and interactions to optimize the user experience and improve date selection functionality.