'Jetpack compose. Date time picker

Does the jetpack compose have the date time picker view or I should create it by myself? I have tried to google it but I couldn't find ready to use component.



Solution 1:[1]

It seems they are still working on it, in the meantime if you need something very simple you can use the inter-ops of compose with Android Views:

@Composable
fun DatePicker(onDateSelected: (LocalDate) -> Unit, onDismissRequest: () -> Unit) {
    val selDate = remember { mutableStateOf(LocalDate.now()) }

    //todo - add strings to resource after POC
    Dialog(onDismissRequest = { onDismissRequest() }, properties = DialogProperties()) {
        Column(
            modifier = Modifier
                .wrapContentSize()
                .background(
                    color = MaterialTheme.colors.surface,
                    shape = RoundedCornerShape(size = 16.dp)
                )
        ) {
            Column(
                Modifier
                    .defaultMinSize(minHeight = 72.dp)
                    .fillMaxWidth()
                    .background(
                        color = MaterialTheme.colors.primary,
                        shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp)
                    )
                    .padding(16.dp)
            ) {
                Text(
                    text = "Select date".toUpperCase(Locale.ENGLISH),
                    style = MaterialTheme.typography.caption,
                    color = MaterialTheme.colors.onPrimary
                )

                Spacer(modifier = Modifier.size(24.dp))

                Text(
                    text = selDate.value.format(DateTimeFormatter.ofPattern("MMM d, YYYY")),
                    style = MaterialTheme.typography.h4,
                    color = MaterialTheme.colors.onPrimary
                )

                Spacer(modifier = Modifier.size(16.dp))
            }

            CustomCalendarView(onDateSelected = {
                selDate.value = it
            })

            Spacer(modifier = Modifier.size(8.dp))

            Row(
                modifier = Modifier
                    .align(Alignment.End)
                    .padding(bottom = 16.dp, end = 16.dp)
            ) {
                TextButton(
                    onClick = onDismissRequest
                ) {
                    //TODO - hardcode string
                    Text(
                        text = "Cancel",
                        style = MaterialTheme.typography.button,
                        color = MaterialTheme.colors.onPrimary
                    )
                }

                TextButton(
                    onClick = {
                        onDateSelected(selDate.value)
                        onDismissRequest()
                    }
                ) {
                    //TODO - hardcode string
                    Text(
                        text = "OK",
                        style = MaterialTheme.typography.button,
                        color = MaterialTheme.colors.onPrimary
                    )
                }

            }
        }
    }
}

@Composable
fun CustomCalendarView(onDateSelected: (LocalDate) -> Unit) {
    // Adds view to Compose
    AndroidView(
        modifier = Modifier.wrapContentSize(),
        factory = { context ->
            CalendarView(ContextThemeWrapper(context, R.style.CalenderViewCustom))
        },
        update = { view ->
            view.minDate = // contraints
            view.maxDate = // contraints

            view.setOnDateChangeListener { _, year, month, dayOfMonth ->
                onDateSelected(
                    LocalDate
                        .now()
                        .withMonth(month + 1)
                        .withYear(year)
                        .withDayOfMonth(dayOfMonth)
                )
            }
        }
    )
}

<style name="CalenderViewCustom" parent="ThemeOverlay.MaterialComponents.MaterialCalendar">
        <item name="colorAccent"><day selection color></item>
        <item name="colorOnPrimary"><text color></item>
        <item name="colorSurface"><background color></item>
</style>

You will end up with a result like this:

enter image description here

Solution 2:[2]

Using a library

As of writing, It's not implemented yet. But there is an open-source library Compose Material Dialogs

Date and Time Picker

Without a library

You can Also do this without a library, you can put it inside onclick action

private fun showDatePicker() {
    val picker = MaterialDatePicker.Builder.datePicker().build()
    activity?.let {
        picker.show(it.supportFragmentManager, picker.toString())
        picker.addOnPositiveButtonClickListener {

        }
    }
}

The old way :D

Solution 3:[3]

Currently, jetpack compose is in Alpha state and there is no DateTime Picker in androidx.ui.*. But, Later android team will add it or make jetpack compose to interoperate with other components like android.widget.DatePicker Or we can develop beautiful Date Time Picker from stratch using jetpack compose like something flutter does

For now,

class DateTimeActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val c = Calendar.getInstance()
        val year = c.get(Calendar.YEAR)
        val month = c.get(Calendar.MONTH)
        val day = c.get(Calendar.DAY_OF_MONTH)
        val mHour = c[Calendar.HOUR_OF_DAY]
        val mMinute = c[Calendar.MINUTE]

        val datePickerDialog = DatePickerDialog(
            this, DatePickerDialog.OnDateSetListener
            { datePicker: DatePicker, day: Int, month: Int, year: Int ->
                setContent {
                    Column {
                        Text("$day, $month, $year")
                    }
                }
            }, year, month, day
        )


        val timePickerDialog = TimePickerDialog(
            this,
            TimePickerDialog.OnTimeSetListener { view, hourOfDay, minute ->
                setContent {
                    Column {
                        Text("$hourOfDay:$minute")
                    }
                }
            }, mHour, mMinute, false
        )


        setContent {
            Column {
                Button(text = "Date",
                    style = OutlinedButtonStyle(),
                    onClick = {
                        datePickerDialog.show()
                    })
                Button(text = "Time",
                    style = OutlinedButtonStyle(),
                    onClick = {
                        timePickerDialog.show()
                    })
            }
        }
    }
}

Solution 4:[4]

You can show date picker with few line in jetpack compose

 AndroidView(
            { CalendarView(it) },
            modifier = Modifier.wrapContentWidth(),
            update = { views ->
                views.setOnDateChangeListener { calendarView, i, i2, i3 ->
                }
            }
        )

Solution 5:[5]

Without 3rd party library, Using Android View solution:

import android.app.TimePickerDialog

@Composable
fun ShowTimePicker(context: Context, initHour: Int, initMinute: Int) {
    val time = remember { mutableStateOf("") }
    val timePickerDialog = TimePickerDialog(
        context,
        {_, hour : Int, minute: Int ->
            time.value = "$hour:$minute"
        }, initHour, initMinute, false
    )
    Button(onClick = {
        timePickerDialog.show()
    }) {
        Text(text = "Open Time Picker")
    }
}

UI Effect

Thanks to Kiran-Bahalaskar

Solution 6:[6]

You could do something like this

import androidx.annotation.StringRes
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.remember
import com.google.android.material.datepicker.CalendarConstraints
import com.google.android.material.datepicker.MaterialDatePicker
import com.google.android.material.datepicker.MaterialPickerOnPositiveButtonClickListener
import java.util.*
import kotlin.time.Duration.Companion.hours
import kotlin.time.DurationUnit

@Composable
fun rememberDatePickerDialog(
    @StringRes title: Int,
    select: Date? = null,
    bounds: CalendarConstraints? = null,
    onDateSelected: (Date) -> Unit = {},
): MaterialDatePicker<Long> {
    val datePicker = remember {
        MaterialDatePicker.Builder.datePicker()
            .setSelection((select?.time
                ?: Date().time) + 24.hours.toLong(DurationUnit.MILLISECONDS))
            .setCalendarConstraints(bounds)
            .setTitleText(title)
            .build()
    }

    DisposableEffect(datePicker) {
        val listener = MaterialPickerOnPositiveButtonClickListener<Long> {
            if (it != null) onDateSelected(Date(it))
        }
        datePicker.addOnPositiveButtonClickListener(listener)
        onDispose {
            datePicker.removeOnPositiveButtonClickListener(listener)
        }
    }

    return datePicker
}

FragmentManager for showing the picker (on button click). Make sure your activity extends from a FragmentActivity before using the following.

@Composable
fun rememberFragmentManager(): FragmentManager {
    val context = LocalContext.current
    return remember(context) {
        (context as FragmentActivity).supportFragmentManager
    }
}

Example use case.

val DATE_FORMAT = SimpleDateFormat("dd/MM/yyyy", Locale.getDefault())
var date by remember {
  mutableStateOf(TextFieldValue(DATE_FORMAT.format(Date())))
}
val fragmentManager = rememberFragmentManager()

val datePicker = rememberDatePickerDialog(
    select = DATE_FORMAT.parse(date.text),
    title = R.string.select_date,
) { date = TextFieldValue(DATE_FORMAT.format(it)) }

TextField(
  readOnly = true,
  value = date,
  onValueChange = { date = it },
  trailingIcon = {
    IconButton({ datePicker.show(fragmentManager, "Date") }) {
       Icon(Icons.Default.DateRange,contentDescription = null)
    }
  },
)

Solution 7:[7]

Wrap created MaterialDatePicker (thanks to @Mitch) but also try to search for wrapped Fragment – this is important in case of orientation change – you have to re-attach listeners to the instance if any.

@Composable
fun rememberDatePickerDialog(
    title: String,
    selection: LocalDate? = null,
    bounds: CalendarConstraints? = null,
    onDateSelected: (LocalDate) -> Unit = {},
): DatePicker {
    val fm = rememberFragmentManager()
    val tag = "date_picker"
    val datePicker = remember {
        @Suppress("UNCHECKED_CAST")
        fm.findFragmentByTag(tag) as? MaterialDatePicker<Long> ?: createMaterialDatePicker(
            selection = selection,
            bounds = bounds,
            title = title
        )
    }

    DisposableEffect(datePicker) {
        val listener = MaterialPickerOnPositiveButtonClickListener<Long> {
            if (it != null) onDateSelected(Instant.ofEpochMilli(it).toLocalDateUTC())
        }
        datePicker.addOnPositiveButtonClickListener(listener)
        onDispose {
            datePicker.removeOnPositiveButtonClickListener(listener)
        }
    }

    return DatePicker(datePicker, fm, tag)
}

class DatePicker(
        private val materialDatePicker: MaterialDatePicker<Long>,
        private val fragmentManager: FragmentManager,
        private val tag: String,
    ) {
    
        fun show() = materialDatePicker.show(fragmentManager, tag)
    
        fun hide() = materialDatePicker.dismiss()
}

private fun createMaterialDatePicker(
    title: String,
    selection: LocalDate?,
    bounds: CalendarConstraints?,
) = MaterialDatePicker.Builder.datePicker()
    .setSelection(selection?.toEpochMillisUTC())
    .setCalendarConstraints(bounds)
    .setTitleText(title)
    .build()


@Composable
fun rememberFragmentManager(): FragmentManager {
    val context = LocalContext.current
    return remember(context) {
        (context as FragmentActivity).supportFragmentManager
    }
}

Solution 8:[8]

I have created a small library for rendering the calendar in Compose: https://github.com/boguszpawlowski/ComposeCalendar. It isn't the date picker per se, but you can configure it to behave like one.

Solution 9:[9]

you can use DatePickerDialog

val calendar = Calendar.getInstance()
            val listener = DatePickerDialog.OnDateSetListener { view, year, month, dayOfMonth ->
                calendar[Calendar.DAY_OF_MONTH] = dayOfMonth
                calendar[Calendar.MONTH] = month
                calendar[Calendar.YEAR] = year
                
                selectedDate = calendar.timeInMillis
            }
            
            DatePickerDialog(
                context,
                listener,
                calendar[Calendar.YEAR],
                calendar[Calendar.MONTH],
                calendar[Calendar.DAY_OF_MONTH]
            ).show()

Solution 10:[10]

As far as I could find, there are still lots of widgets that are missing in Jetpack Compose. The only way to implement them is by using AndroidView and inflating layouts. Here's my code for the time picker: XML file:

    <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TimePicker
        android:id="@+id/simpleTimePicker"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:datePickerMode="spinner"
        android:timePickerMode="spinner"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/btn_set_time"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Set Time"
        android:textColor="#FFFFFF"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/simpleTimePicker"
        tools:ignore="TextContrastCheck" />

    <Button
        android:id="@+id/btn_cancel"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Cancel"
        android:textColor="#FFFFFF"

        app:layout_constraintEnd_toStartOf="@+id/btn_set_time"
        app:layout_constraintTop_toBottomOf="@+id/simpleTimePicker"
        tools:ignore="TextContrastCheck" />
    </androidx.constraintlayout.widget.ConstraintLayout>

And also my code in Jetpack compose window in Kotlin:

 Dialog(onDismissRequest = { timePickerStart = false }) {
                        AndroidView(
                            factory = { context: Context ->
                                LayoutInflater.from(context).inflate(R.layout.customtimepicker, null, false)
                            },
                            modifier = Modifier.fillMaxWidth()
                                .background(Color.White),
                            update = { view ->
                                val setTimebutton = view.findViewById<Button>(R.id.btn_set_time)
                                val cancelButton = view.findViewById<Button>(R.id.btn_cancel)
                                val simpleTimePicker = view.findViewById<TimePicker>(R.id.simpleTimePicker)
                                simpleTimePicker.setIs24HourView(true)
                                setTimebutton.setOnClickListener {
                                    var hour = simpleTimePicker.hour
                                    var minute = simpleTimePicker.minute
                                    addNotePlusViewModel.startTime.value = convertToInstant(hour, minute)
                                    timePickerStart = false
                                }
                                cancelButton.setOnClickListener {
                                    timePickerStart = false
                                }
                            }
                        )
                    }

Solution 11:[11]

I think the best way to add a date picker in Compose is to leverage what Compose already has.

For instance, for a "select" control, we have the ExposedDropdownMenuBox which expects a Composable possessing a TextField and ExposedDropdownMenuItem.

We can follow suit with that to create a DatePickerSelect. You could even reuse most of it if you want to custom implement the picker's calendar interface in a dropdown menu. However, we can minimize the impementation to just a textfield and make use of the circumstances of the DatePicker dialog in building an attractive control.

So, this Compose-like implementation would go as follows:

Usage.kt

/* I used the following imports */
import android.icu.util.Calendar
import android.app.DatePickerDialog

// ...

@Composable
fun SomeScreenContent(
 // ...
) {

    // We need an instance of the calendar utility
    val calendar = Calendar.getInstance()

    // Variable to store the DatePicker selection
    var selectedDate by rememberSaveable{ mutableStateOf( "" ) }
    
    // Helper for the dialog
    val pickedDate = dateStringParser( calendar, selectedDate )

    /* NOTE: Extract the listener
       We don't have to separate the listener, but by doing so we can wrap
       this implementation into a Composable implementation, where we would 
       need to pass this listener and the selectedRange variable into said 
       Composable. I'll show this later on
    */

    val onSelectionChanged = DatePickerDialog.OnDateSetListener {
        _, year, month, dayOfMonth ->
        calendar[Calendar.YEAR] = year
        calendar[Calendar.MONTH] = month
        calendar[Calendar.DAY_OF_MONTH] = dayOfMonth

        // Add 1 to the month so it looks right to the user, since
        // it is actually the months indice, not its value
        selectedDate = "${calendar[Calendar.MONTH] + 1}/${calendar[Calendar.DAY_OF_MONTH]}/${calendar[Calendar.YEAR]}"
    }

    // For supporting text input
    val onTextChanged: ( String ) -> Unit = { newValue -> selectedDate = newValue }

    // The dialog itself, ensured a default - but also that it'll reflect
    // a previously chosen date if already set
    val dialog = DatePickerDialog(
        LocalContext.current,
        onSelectionChanged,
        pickedDate[0],
        pickedDate[1],
        pickedDate[2]
    )

    // Helper for the control ("DatePicker" field) display
    val onClicked = { 
        dialog.show()
    }

    // Now we can present our date picker
    OutlinedTextField(
        value = selectedDate,
        onValueChange = onTextChanged,
        label = { Text( "Select Date" ) },
        leadingIcon = { 
            Icon( 
                Icons.Default.CalendarViewMonth, 
                contentDescription = null,
                modifier = Modifier
                    .clickable(
                        enabled = true,
                        role = Role.Button,
                        onClick = onClicked
                    ) 
            )
        },
        trailingIcon = {
            Icon( 
                Icons.Default.TouchApp, 
                contentDescription = null,
                modifier = Modifier
                    .clickable(
                        enabled = true,
                        role = Role.Button,
                        onClick = onClicked
                    ) 
            )
        },
        readOnly = false,
        modifier = Modifier // Whatever you want here
    )
}


/**
 * A helper method to handle formatting of visual elements that are leveraged
 * internally in logic
 *
 * @param calendar [Calendar] An ICU compatible calendar instance
 * @param date [String] A formatted date string (i.e. 01/01/2001)
 *
 * @returns [Array] An array of integers that represent the day, month, and
 *                  year of a particular calendar date.
 */
fun dateStringParser( calendar: Calendar, date: String ): Array<Int> {
    // A default
    var day = calendar[Calendar.DAY_OF_MONTH]
    var month = calendar[Calendar.MONTH]
    var year = calendar[Calendar.YEAR]

    // Leverage picked date to open our calendar where we last picked
    // but don't attempt to split on a character that doesnt exist
    if( date.isNotEmpty() && date.isNotBlank() && date.contains( "/" ) {
        val breakdown = date.split( "/" )

        // Then ensure each part has a value (isn't null or blank)
        if( breakdown.count() == 3 &&
            breakdown[0].isNotEmpty() && breakdown[0].isNotBlank() &&
            breakdown[1].isNotEmpty() && breakdown[1].isNotBlank() &&
            breakdown[2].isNotEmpty() && breakdown[2].isNotBlank()
        ) {
            // Make sure we handle the month properly. We must add
            // a 1 to display as text properly, but must restore it
            // for the picker dialog to interpret it properly, since
            // the months are indices, and not their actual value.
            day = breakdown[1].toInt()
            month = breakdown[0].toInt() - 1
            year = breakdown[2].toInt()
        }
    }

    return arrayOf( year, month, day )
}

// ...

and that's the basic gist of it.

You'll get the following control:

DatePicker Control/Field (Empty)

Upon clicking the control (in the text input space), you'll get:

Text entry mode w/ keyboard

Upon clicking on either icon:

DatePicker Dialog

After selecting a date (or if prepopulated) you'll get:

DatePicker Control/Field after picking date

With this approach you can obviously just type a quick date, but if you choose to then click on the icons to view the picker it'll reflect what you've typed. It gives a well-rounded implementation.

This is also very easy to make reusable. Mind you, I'm sure there's an even more efficient way, but for the sake of a quick example:

SelectFactory.kt

// ...

/**
 * A DatePicker implementation
 *
 * @param label [String] The label for the DatePicker
 * @param selection [String] The currently seleted date, or a blank string
 * @param onSelectionChanged [DatePickerDIalog.OnDateSetListener]
 * @param onTextChanged [Unit]
 * @param modifier [Modifier] A modifier for the Control/Field
 */
@Composable
fun DatePickerSelect(
    label: String,
    selection: String = "",
    onSelectionChanged: DatePickerDialog.OnDateSetListener = DatePickerDialog.OnDateSetListener{ datePicker, i, i2, i3 ->  },
    onTextChanged: ( String ) -> Unit = {},
    modifier: Modifier = Modifier.fillMaxWidth()
) {
    val calendar = Calendar.getInstance()
    val pickedDate = dateStringParser( calendar = calendar, date = selection )
    val dialog = DatePickerDialog(
        LocalContext.current,
        onSelectionChanged,
        pickedDate[0],
        pickedDate[1],
        pickedDate[2]
    )
    val onClicked = {
        dialog.show()
    }

    OutlinedTextField(
        value = selection,
        onValueChange = onTextChanged,
        label = { Text( label ) },
        leadingIcon = {
            Icon(
                Icons.Default.CalendarViewMonth,
                contentDescription = null,
                modifier = Modifier
                    .clickable(
                        enabled = true,
                        role = Role.Button,
                        onClick = onClicked
                    )
            )
        },
        trailingIcon = {
            Icon(
                Icons.Default.TouchApp,
                //when( isExpanded ){ // To use arrows instead
                //    true -> Icons.Default.ArrowUpward 
                //    false -> Icons.Default.ArrowDownward
                //},
                contentDescription = null,
                modifier = Modifier
                    .clickable(
                        enabled = true,
                        role = Role.Button,
                        onClick = onClicked
                    )
            )
        },
        readOnly = false,
        modifier = modifier
    )
}

    /* NOTE: Regarding commented expanded parameters
     *
     * Uncomment and use all commented "expanded"-related parameters
     * to track and implement arrow indicators, etc. I prefer the
     * the touch icon since you wouldn't see the arrows under the
     * the dialog anyways.
     *
     * You'd need to add a parameter to the DatePickerSelect 
     * Composable for both tracking the Boolean and an 
     * onExpandedChanged Unit, make use of those variables where you 
     * instantiate it and within the method, with remembered mutable 
     * state...and then you can easily track a toggleable icon set.
     */

// ...

fun dateStringParser( calendar: Calendar, date: String ): Array<Int> {
    // The same method as reflected in above sample, copy it here...
}

// ...

    /* NOTE: Regarding this next method
     * 
     *   The following method is just candy, to provide a complete
     *   solution if you borrow this implementation, that will give
     *   everything needed to fetch a proper epoch to run queries
     *   with based on the potentially text-input date string
     *
     */

/**
 * A helper method to handle converting a formatted date string to 
 * its respective epoch
 *
 * @param date [String] A formatted date string (i.e. 01/01/2001)
 *
 * @returns [Long] The epoch represented by the provided formatted 
 *                 date string
 */
fun dateStringConverter( date: String ): Long {
    val calendar = Calendar.getInstance()

    if( date.isNotEmpty() && date.isNotBlank() && date.contains( "/" ) ) {
        val breakdown = date.split( "/" )

        if( breakdown.count() == 3 &&
            breakdown[0].isNotEmpty() && breakdown[0].isNotBlank() &&
            breakdown[1].isNotEmpty() && breakdown[1].isNotBlank() &&
            breakdown[2].isNotEmpty() && breakdown[2].isNotBlank()
        ) {
            // Again we need to subtract 1 from the month, so that
            // our calendar instance can provide us with a proper
            // epoch, considering the months are indices, and not
            // actual values
            calendar[Calendar.DAY_OF_MONTH] = breakdown[1].toInt()
            calendar[Calendar.MONTH] = breakdown[0].toInt() - 1
            calendar[Calendar.YEAR] = breakdown[2].toInt()
        }
    }

    return calendar.timeInMillis
}

Then, wherever you want to use it, you just need the minimal setup:

Implementation.kt

// ...

// The prep
val calendar = Calendar.getInstance()
var selected by rememberSaveable{ mutableStateOf( "" ) }

val listener = DatePickerDialog.OnDateSetListener { 
    _, year, month, dayOfMonth ->
    calendar[Calendar.DAY_OF_MONTH] = dayOfMonth
    calendar[Calendar.MONTH] = month
    calendar[Calendar.YEAR] = year

    // Add 1 to the month, because it's indexed, not actual
    selected = "${calendar[Calendar.MONTH] + 1}/${calendar[Calendar.DAY_OF_MONTH]}/${calendar[Calendar.YEAR]}"
}

val onTextChanged: ( String ) -> Unit = { newValue -> selected = newValue } 

// Then spit it out:
DatePickerSelect(
    label = "Start Date",
    selection = selectedDate,
    onSelectionChanged = listener,
    onTextChanged = onTextChanged,
    modifier = Modifier // fillMaxWidth( 0.5f ) for first one 
)                       // fillMaxWidth() (is the default) for the second

// ...

Lastly, the time picker dialog functions in the exact same way - so you can combine it with this, or make another control separate for it. You'd just split on colons rather than slashes, etc.

Solution 12:[12]

Used @Chandler's code to implement this:


@Preview()
@Composable
fun MyDatePickerPreview() {
    val dp = rememberMyDatePicker(initialDate = Date(), onDateChange = {})
    dp.show()
}

@Composable
fun rememberMyDatePicker(
    initialDate: Date = Date(),
    onDateChange : (newDate: Date) -> Unit
) : DatePickerDialog {
    val context = LocalContext.current
    val initialLocalDate = localDateFor(initialDate)
    val initialYear = initialLocalDate.year
    val initialMonth = initialLocalDate.monthValue - 1 // month 5 is june in  java calendar
    val initialDayOfMonth = initialLocalDate.dayOfMonth
    println("Initial year $initialYear month $initialMonth day $initialDayOfMonth")
    val datePickerDialog = DatePickerDialog(
        context,
        { _, year: Int, month: Int, dayOfMonth: Int ->
            val newLocalDate = LocalDate.of(year, month + 1, dayOfMonth)
            println("Year $year Month $month Day $dayOfMonth")
            onDateChange(dateFor(newLocalDate))
        },
        initialYear, initialMonth, initialDayOfMonth
    )
    return remember { datePickerDialog }
}

fun dateFor(ld: LocalDate): Date {
    return Date.from(
        ld.atStartOfDay()
            .atZone(ZoneId.systemDefault())
            .toInstant()
    )
}

fun localDateFor(date: Date): LocalDate {
    return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate()
}