Today, almost every android application has some kind of date or time picker implemented. We often use them to set reminders, mark special dates in the calendar, set up an alarm, etc… Nowadays almost every app developer tries to follow the Material guidelines and luckily with the recent release of Google Material Design library we can use the MaterialDatePicker component. It a new component that has very compelling features including the following list.

Before showing you the features you need to add the updated Material Design library in the app-level build.gradle file.

implementation 'com.google.android.material:material:1.2.0-alpha04'

Table of contents:

This Material DatePicker is the default one (Mostly used when you want to select only one date).

val builder : MaterialDatePicker.Builder<*> = MaterialDatePicker.Builder.datePicker() // 1 val picker : MaterialDatePicker<*> = builder.build() // 2 picker.show(supportFragmentManager, picker.toString()) // 3

Here, what’s going on inside the above code.

Creating a MaterialDatePicker.Builder instance that gives us a single date selector. Building-up the MaterialDatePicker by calling a builder.build function. Display the Material Date Picker.

Here’s what it looks like!

If you look at the OK (Positive) button at the bottom you’ll notice that it’s by default not enable until we select the date from the Date Picker. So, here’s how to select the default date when the dialog shows.

val builder : MaterialDatePicker.Builder<*> = MaterialDatePicker.Builder.datePicker() val currentTimeInMillis = Calendar.getInstance().timeInMillis builder.setSelection(currentTimeInMillis) val picker : MaterialDatePicker<*> = builder.build() picker.show(supportFragmentManager, picker.toString())

Now if we run the above code the Android Material Date Picker by-default shows with the today date as selected.

That’s cool! I think at this point you guys understand how simple it is to show a Date Picker using Material Design Library. Let’s take this example a little further and see how we can restrict the user to select a date within the start and end date.

We often have this business logic to only show the calendar dates within the date ranges and it’s a nightmare to do it. Well Not Now!

val constraintsBuilder = CalendarConstraints.Builder() // 1 val calendar = Calendar.getInstance() constraintsBuilder.setStart(calendar.timeInMillis) // 2 calendar.roll(Calendar.YEAR, 1) // 3 constraintsBuilder.setEnd(calendar.timeInMillis) // 4 builder.setCalendarConstraints(constraintsBuilder.build()) // 5 val picker: MaterialDatePicker<*> = builder.build() picker.show(supportFragmentManager, picker.toString())

Here’s what we’re doing in the above code.

Creating a new instance of CalendarConstranints.Builder class. This class used to limit the display range. Setting out the start date as the current date. Now the Date Picker will use the current month as a start of the year. Sets the new date to the calendar instance by adding the 1 year. So, that we can restrict user within one month. Sets the end date. Finally, build the constraints using a set of parameters we passed to it.

Now we only allow the user to select a date within a year.

Valid days are like to only allow the user to select a date or range of dates from the DateValidator. As an example let’s say we want our user to only a select date from working days and disable all the weekend days.

val constraintsBuilder = CalendarConstraints.Builder() constraintsBuilder.setValidator(WeekDayValidator()) // 1 builder.setCalendarConstraints(constraintsBuilder.build()) val picker: MaterialDatePicker<*> = builder.build() picker.show(supportFragmentManager, picker.toString())

The only noticeable thing in the above code is the WeekDayValidator class.

Here is the WeekDayValidator class implementation.

class WeekDayValidator : CalendarConstraints.DateValidator { private val utc = Calendar.getInstance(TimeZone.getTimeZone("UTC")) val CREATOR: Parcelable.Creator<WeekDayValidator?> = object : Parcelable.Creator<WeekDayValidator?> { override fun createFromParcel(source: Parcel): WeekDayValidator { return WeekDayValidator() } override fun newArray(size: Int): Array<WeekDayValidator?> { return arrayOfNulls(size) } } override fun writeToParcel(dest: Parcel?, flags: Int) { } override fun isValid(date: Long): Boolean { utc.timeInMillis = date val dayOfWeek = utc[Calendar.DAY_OF_WEEK] return dayOfWeek != Calendar.SATURDAY && dayOfWeek != Calendar.SUNDAY } override fun describeContents(): Int { return 0 } override fun hashCode(): Int { val hashedFields = arrayOf<Any>() return hashedFields.contentHashCode() } }

You see in the above demo all the Saturday’s and Sunday’s are disabled.

If we need a MaterialDatePicker where our user can select a range of dates then we can do that like so:

val builder: MaterialDatePicker.Builder<Pair<Long, Long>> = MaterialDatePicker.Builder.dateRangePicker() val picker: MaterialDatePicker<*> = builder.build() picker.show(supportFragmentManager, picker.toString())

As a result of the above code, we get a date picker like this.

Input Modes

The Google Material Library also allows manual entry using the numbers to input the date for Material Date Picker. Users can input a date or range of dates.

Here are a couple of input modes available.

INPUT_MODE_CALENDAR (default)

INPUT_MODE_TEXT

So, in order to show input mode for the date picker, we need to set it with the setInputMode method.

val builder: MaterialDatePicker.Builder<Pair<Long, Long>> = MaterialDatePicker.Builder.datePicker() builder.setInputMode(MaterialDatePicker.INPUT_MODE_TEXT) val picker: MaterialDatePicker<*> = builder.build() picker.show(supportFragmentManager, picker.toString())

So, after showing the dialog we need to know what happens when a user interacts with the date picker.

There are three listeners added for Material Date Picker.

addOnCancelListener: Called when a user press on outside date picker and dialog gets canceled.

picker.addOnCancelListener { toast("dialog canceled") }

addOnNegativeButtonClickListener: Triggers when the dialog negative button clicked.

picker.addOnNegativeButtonClickListener { toast("dialog negative button clicked") }

addOnPositiveButtonClickListener: A successful when a user selects the date or date ranges.

picker.addOnPositiveButtonClickListener { val (startOfRange, endOfRange) = it // in case date range operation val date = it // single select date }

Apply Theming

In last I just wanna show you guys how we can change the Material Date Picker dialog to fullscreen and revert it to normal dialog.

Here are a couple of material themes available.

R.attr.materialCalendarTheme (default)

R.attr.materialCalendarFullscreenTheme

private fun resolveOrThrow(context: Context, @AttrRes attributeResId: Int): Int { val typedValue = TypedValue() if (context.theme.resolveAttribute(attributeResId, typedValue, true)) { return typedValue.data } throw IllegalArgumentException(context.resources.getResourceName(attributeResId)) } val fullscreenTheme = resolveOrThrow(getContext(), R.attr.materialCalendarFullscreenTheme) val builder: MaterialDatePicker.Builder<Pair<Long, Long>> = MaterialDatePicker.Builder.datePicker() builder.setTheme(fullscreenTheme) val picker: MaterialDatePicker<*> = builder.build() picker.show(supportFragmentManager, picker.toString())

That’s all guys, thanks for reading this till the end. If you like what you read please share it with the community and don’t forget to hit the ♥️ button below. Please provide feedback if you think this article can be improved.

I also wrote a couple of articles on Material Design Components in case you wanna check out. Here is the link.

Thank you for being here and keep reading…