Alexandru Gheorghe
07/29/2024, 2:33 PMrememberTimePicker
and rememberDatePicker
, respectively. To make date reflect new chosen date by user, I've to manually do a LaunchedEffect on the date (as stored in the ViewModel) and update the DatePickerState accordingly. However, TimePickerState does not have a way to update the time. Has anyone else ran into this and if so, how did you work around it?Chrimaeon
07/29/2024, 3:29 PMChrimaeon
07/29/2024, 3:30 PMAlexandru Gheorghe
07/29/2024, 3:31 PMChrimaeon
07/29/2024, 3:33 PMinitalValue
to your “wrapper” composable
@Composable
fun TimeDailer(
onConfirm: (TimePickerState) -> Unit,
onDismiss: () -> Unit,
modifier: Modifier = Modifier,
initialTime: LocalTime =
Clock.System
.now()
.toLocalDateTime(TimeZone.currentSystemDefault())
.time,
) {
val timePickerState =
rememberTimePickerState(
initialHour = initialTime.hour,
initialMinute = initialTime.minute,
is24Hour = true,
)
var hasError by remember {
mutableStateOf(false)
}
and then when where you show the `TimeDailer`:Alexandru Gheorghe
07/29/2024, 3:35 PMChrimaeon
07/29/2024, 3:37 PMMutableState
or something like this that you then “observe” in the composable to get the updated values.
check the code lab: https://developer.android.com/codelabs/basic-android-kotlin-compose-viewmodel-and-state#5Alexandru Gheorghe
07/29/2024, 3:48 PMChrimaeon
07/29/2024, 3:48 PMsome code snippets will help to see your “architecture”.
Alexandru Gheorghe
07/30/2024, 7:01 AM// ViewModel
data class TimeClass(
val hour: Int = Calendar.getInstance().get(Calendar.HOUR_OF_DAY),
val minute: Int = Calendar.getInstance().get(Calendar.MINUTE)
)
class MyViewModel : ViewModel() {
private val _object = mutableStateOf(Object()) // updated asynchronously before navigating to composable screen
val object = _object
private val _selectedTime = mutableSetOf(TimeClass())
val selectedTime = _selectedTime
fun updateObject(object: Object) {
_object.value = object
}
fun updateTime(hour: Int, minute: Int) {
val calendar = Calendar.getInstance().apply {
set(Calendar.HOUR_OF_DAY, hour)
set(Calendar.MINUTE, minute)
}
_selectedTime.value = TimeClass(
calendar.get(Calendar.HOUR_OF_DAY),
calendar.get(Calendar.MINUTE)
)
}
Then, in my composable:
@Composable
fun MyComposable(modifier: Modifier = Modifier, myViewModel: MyViewModel) {
val object = myViewModel.object.value
LaunchedEffect(object) {
if (object.time != null) {
myViewModel.updateTime(object.time.hour, object.time.minute)
}
}
DateTimePicker(modifier = modifier, myViewModel = myViewModel)
}
@Composable
fun DateTimePicker(modifier..., myViewModel: MyViewModel) {
val time = myViewModel.selectedTime.value
val timePickerState = rememberTimePickerState(
initialHour = time.hour,
initialMinute = time.minute
)
TimePickerDialog(...) {
TimePicker(state = timePickerState)
}
}
Where TimePickerDialog
is of the nature of https://developer.android.com/develop/ui/compose/components/time-pickers-dialogs#advanced
Outside of these.composables, there's an asynchronous update to object
that's stored in myViewModel. The idea is: when this object is updated, the time is updated which should be reflected in the rememberTimePickerState
. But it's not. TimePicker remains with default (system) values.Chrimaeon
07/30/2024, 10:17 AMLaunchedEffect(object) {
if (object.time != null) {
myViewModel.updateTime(object.time.hour, object.time.minute)
}
}
will update the time in the view model but at that time your dialog is already visible with the old value and you do not receive the updated value in the DateTimePicker
.
Try logging the time
in the DateTimePicker
.
LaunchedEffect(time) {
Log.v("DatePicker", time.toString())
}
I guess you’ll see the time updated in the logs.Alexandru Gheorghe
07/30/2024, 10:40 AMrememberTimePickerState
recompose with the new values since the ViewModel stores the parameter as mutableStateOf()
?Alexandru Gheorghe
07/30/2024, 10:40 AMChrimaeon
07/30/2024, 10:46 AMShouldn’tas its “remembering” it does not because you cannot provide a key.recompose with therememberTimePickerState
And how do I make sure I navigate to the screen only when the asynchronous calls are done?create a loading state and open the screen after “loading” is complete.
Chrimaeon
07/30/2024, 10:47 AMAlexandru Gheorghe
07/30/2024, 10:47 AMAlexandru Gheorghe
07/30/2024, 10:48 AMChrimaeon
07/30/2024, 10:48 AMChrimaeon
07/30/2024, 10:48 AMChrimaeon
07/30/2024, 10:48 AMAnd how do I make sure I navigate to the screenyou refer to it as screen.
Chrimaeon
07/30/2024, 10:49 AMif (loadingState == complete){
DateTimePicker(...)
}
Alexandru Gheorghe
07/30/2024, 10:50 AMChrimaeon
07/30/2024, 10:50 AMChrimaeon
07/30/2024, 10:50 AMCPI
though.Alexandru Gheorghe
07/30/2024, 10:51 AMAlexandru Gheorghe
07/30/2024, 10:51 AMChrimaeon
07/30/2024, 10:52 AMAlexandru Gheorghe
07/30/2024, 10:52 AMAlexandru Gheorghe
07/30/2024, 10:53 AMwhen(UiState)
for another iteration in the future if the feature request doesn't get done by then (app is not in production)Chrimaeon
07/30/2024, 10:54 AMAlexandru Gheorghe
08/06/2024, 7:03 PM