Search code examples
kotlinandroid-jetpack-composekotlin-coroutinesandroid-viewmodelkotlin-flow

Jetpack Compose: Notify changes in StateFlow


I have a view model that gets search query data string every time a user enter a text in my text field in the Top App Bar.

The view model is as follows:

ViewModel.kt

class ServicesViewModel(
    private val offlineServiceRepository:ServiceRepository
) : ViewModel() {

    private var searchQuery = mutableStateOf("")

    val servicesUiState: StateFlow<ServicesUiState> = offlineServiceRepository.search(searchQuery.value).map {

        Log.i("SEARCHVALUE", "UPDATE SF: ${searchQuery.value}")
        ServicesUiState.Success(it)
    }
        .stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(TIMEOUT_MILLIS),
            initialValue = ServicesUiState.Loading
        )


fun updateSearchQuery(query: String){
    searchQuery.value = query
    Log.i("SEARCHVALUE", "SQ: ${searchQuery.value}")
}

Then in my screen:

@Composable
fun ScreenServices(
    viewModel: ServicesViewModel,
    retryAction: () -> Unit,
    navigateToPreCall: (service: Service) -> Unit,
    searchBarValue: String,
    modifier: Modifier = Modifier
) {

    val servicesUiState by viewModel.servicesUiState.collectAsState()

    viewModel.updateSearchQuery(searchBarValue)

    when (servicesUiState) {
        is ServicesUiState.Loading -> LoadingScreen(modifier)
        is ServicesUiState.Success -> ServicesList((servicesUiState as ServicesUiState.Success).services, navigateToPreCall, modifier)
        is ServicesUiState.Error -> ErrorScreen(retryAction, modifier)
    }
}

The searchBarValue is being sent successfully from my UI screen to the viewModel every time the user types a new text and the searchQuery variable is updated accordingly. However the servicesUiState isn't getting the updated searchQuery.value and hence the list is not being updated on my UI.

Can someone help out on how i can pass the searchQuery variable to my servicesUiState StateFlow variable in my view model ?


Solution

  • (Mutable)State is a value holder class that could be observed for changes and comes with jetpack compose. It is similar to kotlin Flow. When you use it in a @Composable function, it will be automatically observed and your @Composable function will be recomposed on every change. You are not using it in a composable function, so you have to observe it yourself. One way to do that would be using snapshotFlow function to convert it to Flow:

    private val searchQueryFlow = snapshotFlow { searchQuery.value }
    val serviceUiState = searchQueryFlow.map { query ->
        offlineServiceRepository.search(query)
    }.map { ... }
    

    In your case though, it will be better to use MutableStateFlow instead, using MutableState here doesn't make much sense.