I'm a little confused with the following scenario:
Initially I am displaying a DatePicker modal on the screen.
When the user selects a date, I display several buttons.
And when the user clicks on any of those buttons, I want to pass to the ViewModel the two pieces of data: the selected date and the type of button that was pressed, to search for data in the repository with those two criteria.
My problem is that I can't find a way to pass the two pieces of data, before the ViewModel is included.
I'm trying to do something like InterestsScreen
in Now In Android.
I have a similar code in another scenario where it works, but there the date is taken from the system, so there is no problem. Here the date must be given by the user, from a DatePicker.
I don't show the code for the UseCase or the Repository, that code works fine when I pass it the correct parameters. My problem is that I don't know how to make the call to the ViewModel when I have the selected date and the type of button that the user has pressed.
I show the relevant parts of my code:
NavGraphBuilder
composable<CalendarRoute> {
CalendarScreen(onTopicClick = onTopicClick,onDateSelected={})
CalendarScreen
(called from NavGraphBuilder)@Composable
fun CalendarScreen(
onTopicClick: (String) -> Unit,
onDateSelected: (Long?) -> Unit,
viewModel: CalendarViewModel = hiltViewModel()
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
DatePickerAndButtons(
onTopicClick = viewModel::onTopicClick,
onDateSelected = viewModel::onDateSelected,
uiState = uiState
)
}
DatePickerAndButtons
@ExperimentalLayoutApi
@Composable
fun DatePickerAndButtons(
onTopicClick: (String) -> Unit,
onDateSelected: (Long?) -> Unit,
uiState: CalendarUiState
) {
var showTopics by remember { mutableStateOf(false) }
var showDatePicker by remember { mutableStateOf(true) }
var selectedDate by remember { mutableStateOf<Long?>(null) }
when (uiState) {
CalendarUiState.Empty -> Text("Empty")
CalendarUiState.Error -> Text("Error")
CalendarUiState.Loading -> {
Text("Loading")
//showDatePicker=true
}
is CalendarUiState.CalendarData -> {
showTopics=false
//showDatePicker=false
if(uiState.topics.isNotEmpty()) {
//Show data
}else{
//¿?
}
}
}
if (selectedDate != null) {
val date = Date(selectedDate!!)
val formattedDate = SimpleDateFormat("MMM dd, yyyy", Locale.getDefault()).format(date)
Text("Selected date: $formattedDate")
} else {
Text("No date selected")
}
if (showDatePicker) {
DatePickerModal(
onDateSelected = {
selectedDate = it
onDateSelected(it)
//viewModel.onDateSelected(selectedDate)
showDatePicker = false
showTopics=true
},
onDismiss = { showDatePicker = false }
)
}
if(showTopics) {
//Code that create buttons to be clicked
}
}
ViewModel
@HiltViewModel
class CalendarViewModel @Inject constructor(
private val savedStateHandle: SavedStateHandle,
val userDataRepository: UserDataRepository,
val universalisRepository: UniversalisRepository,
getTopicWithDate: GetUniversalisFromCalendarUseCase,
) : ViewModel() {
private val selectedTopicIdKey = "selectedTopicIdKey"
private val selectedDateKey = "selectedDateKey"
private val calendarRoute: CalendarRoute = savedStateHandle.toRoute()
private val selectedTopicId = savedStateHandle.getStateFlow(
key = selectedTopicIdKey,
initialValue = "1" //calendarRoute.topicId,
)
private val selectedDate = savedStateHandle.getStateFlow(
key = selectedDateKey,
initialValue = 0
)
val uiState: StateFlow<CalendarUiState> = combine(
selectedTopicId,
getTopicWithDate.invoke(
date = selectedDate.value,
title = "TODO:Title",
selectedTopicId = selectedTopicId.value
),
CalendarUiState::CalendarData,
).
stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = CalendarUiState.Loading,
)
fun onTopicClick(topicId: String?) {
savedStateHandle[selectedTopicIdKey] = topicId
}
fun onDateSelected(date: Long?) {
savedStateHandle[selectedDateKey] = date
}
}
You have incorrectly combined two different stages of flow.
The topicId
and date
are query parameters for the Topic, so the query only starts when they change.
So your topic query results will be like:
private val topics: Flow<YourTopicsModel> = combine(
selectedDate,
selectedTopicId,
){date,topicId->
date to topicId
}.flatMapLatest {
getTopicWithDate(
date = it.first,
title = "TODO:Title",
selectedTopicId = it.second
)
}
Then your uiState
will be:
val uiState: StateFlow<CalendarUiState> = combine(
selectedTopicId,
topics,
CalendarUiState::CalendarData,
).
stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = CalendarUiState.Loading,
)