I have been searching for this widget for a long time, scoured through the entire forum, but couldn't find a working code. I had to create my own with the help of a YouTube video and GPT.
Sharing the code in case it proves useful to someone.
I'm not a programmer, and I couldn't figure out how to set up the visual display of the button that launches the calendar. If someone understands it better, please share, and I would be grateful.
For proper functioning, an updatePageUI as Action arguments need to be specified.
Add dependencies: calendar_date_picker2: ^0.5.3
Call the widget DateRangePicker
copy-paste the code save and you are ready
// Automatic FlutterFlow imports
import '/backend/backend.dart';
import '/backend/schema/structs/index.dart';
import '/flutter_flow/flutter_flow_theme.dart';
import '/flutter_flow/flutter_flow_util.dart';
import '/custom_code/widgets/index.dart'; // Imports other custom widgets
import '/custom_code/actions/index.dart'; // Imports custom actions
import '/flutter_flow/custom_functions.dart'; // Imports custom functions
import 'package:flutter/material.dart';
// Begin custom widget code
// DO NOT REMOVE OR MODIFY THE CODE ABOVE!
import 'package:calendar_date_picker2/calendar_date_picker2.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
final today = DateUtils.dateOnly(DateTime.now());
class DateRangePicker extends StatefulWidget {
const DateRangePicker({
Key? key,
this.width,
this.height,
required this.updatePageUI,
}) : super(key: key);
final double? width;
final double? height;
final Future Function() updatePageUI;
@override
_DateRangePickerState createState() => _DateRangePickerState();
}
class _DateRangePickerState extends State<DateRangePicker> {
List<DateTime?> _dialogCalendarPickerValue = [
DateTime(2024, 1, 10),
DateTime(2024, 1, 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(
body: Center(
child: SizedBox(
width: 375,
child: ListView(
children: <Widget>[_buildCalendarDialogButton()],
),
),
),
);
}
List<DateTime?> _getValueText(
CalendarDatePicker2Type datePickerType,
List<DateTime?> values,
) {
values =
values.map((e) => e != null ? DateUtils.dateOnly(e) : null).toList();
List<DateTime?> selectedDates = [];
if (datePickerType == CalendarDatePicker2Type.multi) {
selectedDates = values.isNotEmpty ? values : [null];
} else if (datePickerType == CalendarDatePicker2Type.range) {
if (values.isNotEmpty) {
final startDate = values[0];
final endDate = values.length > 1 ? values[1] : null;
FFAppState().startDate = startDate;
FFAppState().endDate = endDate;
selectedDates = [startDate, endDate];
widget.updatePageUI();
}
}
return selectedDates;
}
Widget _buildCalendarDialogButton() {
const dayTextStyle =
TextStyle(color: Colors.white, 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: Color.fromARGB(255, 42, 195, 159),
closeDialogOnCancelTapped: true,
firstDayOfWeek: 1,
weekdayLabelTextStyle: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
controlsTextStyle: const TextStyle(
color: Colors.white,
fontSize: 15,
fontWeight: FontWeight.bold,
),
centerAlignModePicker: true,
customModePickerIcon: const SizedBox(),
selectedDayTextStyle:
dayTextStyle.copyWith(color: Color.fromARGB(255, 252, 252, 252)),
dayTextStylePredicate: ({required date}) {
TextStyle? textStyle;
if (date.weekday == DateTime.saturday ||
date.weekday == DateTime.sunday) {
textStyle = weekendTextStyle;
}
if (DateUtils.isSameDay(date, DateTime(2024, 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
? Color.fromARGB(255, 251, 77, 77)
: Color.fromARGB(255, 227, 227, 227),
),
),
),
],
),
),
);
}
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(1),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () async {
final values = await showCalendarDatePicker2Dialog(
context: context,
config: config,
dialogSize: const Size(325, 400),
borderRadius: BorderRadius.circular(8),
value: _dialogCalendarPickerValue,
dialogBackgroundColor: Color.fromARGB(255, 54, 53, 53),
);
if (values != null) {
// ignore: avoid_print
print(_getValueText(
config.calendarType,
values,
));
setState(() {
_dialogCalendarPickerValue = values;
});
}
},
child: const Text('Range'),
),
],
),
);
}
}
Feel free to change colors in code if needed
PS
Forgot to mention, this widget sets the startDate and endDate, so for usage, you need to create App states with the same names, and they should be in DateTime format. After that, you can use the data from the widget to filter your ListView.
Here is the action setup after adding DatePicker in your UI
Don't know if it's crucial to refresh Database Request but I used it