Home > other >  How to observe a MutableStateFlow list in Jetpack Compose
How to observe a MutableStateFlow list in Jetpack Compose

Time:06-15

I have to implement Google's "Place Autocomplete" on Jetpack Compose, but the problem is that once I get the list of places, I can't update the UI.

Going into more detail, the places received from the google API are stored in a MutableStateFlow <MutableList <String>> and the status is observed in a Composable function via: databaseViewModel.autocompletePlaces.collectAsState(). However, when a new item is added to the list, the Composable function is not re-compiled

Class that gets the places:

class AutocompleteRepository(private val placesClient: PlacesClient)
{
    val autocompletePlaces = MutableStateFlow<MutableList<String>>(mutableListOf())

    fun fetchPlaces(query: String) {
        val token = AutocompleteSessionToken.newInstance()

        val request = FindAutocompletePredictionsRequest.builder()
            .setSessionToken(token)
            .setCountry("IT")
            .setQuery(query)
            .build()

        placesClient.findAutocompletePredictions(request).addOnSuccessListener {
                response: FindAutocompletePredictionsResponse ->
            autocompletePlaces.value.clear()
            for (prediction in response.autocompletePredictions) {
                autocompletePlaces.value.add(prediction.getPrimaryText(null).toString())
            }
        }
    }
}

ViewModel:

class DatabaseViewModel(application: Application): AndroidViewModel(application) {

    val autocompletePlaces: MutableStateFlow<MutableList<String>>
    val autocompleteRepository: AutocompleteRepository

    init {
        Places.initialize(application, apiKey)
        val placesClient = Places.createClient(application)
        autocompleteRepository = AutocompleteRepository(placesClient)
        autocompletePlaces = autocompleteRepository.autocompletePlaces
    }

    fun fetchPlaces(query: String)
    {
        autocompleteRepository.fetchPlaces(query)
    }
}

Composable function:

@Composable
fun dropDownMenu(databaseViewModel: DatabaseViewModel) {

    var placeList = databaseViewModel.autocompletePlaces.collectAsState()
    
    //From here on I don't think it's important, but I'll put it in anyway:
    var expanded by rememberSaveable { mutableStateOf(true) }
    var placeName by rememberSaveable { mutableStateOf("") }

    Column {
        OutlinedTextField(value = placeName, onValueChange =
        { newText ->
            placeName = newText
            databaseViewModel.fetchPlaces(newText)
        })

        DropdownMenu(expanded = expanded,
            onDismissRequest = { /*TODO*/ },
            properties = PopupProperties(
                focusable = false,
                dismissOnBackPress = true,
                dismissOnClickOutside = true)) {
            placeList.value.forEach { item ->
                DropdownMenuItem(text = { Text(text = item) },
                    onClick = {
                        placeName = item
                        expanded = false
                    })
            }
        }
    }
}

EDIT: solved by changing: MutableStateFlow<MutableList<String>>(mutableListOf()) to MutableStateFlow<List<String>>(listOf()), but I still can't understand what has changed, since the structure of the list was also changed in the previous code

class AutocompleteRepository(private val placesClient: PlacesClient)
{
    val autocompletePlaces = MutableStateFlow<List<String>>(listOf()) //Changed

    fun fetchPlaces(query: String) {
        val token = AutocompleteSessionToken.newInstance()

        val request = FindAutocompletePredictionsRequest.builder()
            .setSessionToken(token)
            .setCountry("IT")
            .setQuery(query)
            .build()

        val temp = mutableListOf<String>()
        placesClient.findAutocompletePredictions(request).addOnSuccessListener {
                response: FindAutocompletePredictionsResponse ->
            for (prediction in response.autocompletePredictions) {
                temp.add(prediction.getPrimaryText(null).toString())
            }
            autocompletePlaces.value = temp //changed
        }
    }
}

CodePudding user response:

In your old code you were just changing the MutableList instance inside the StateFlow by adding items to it. This does not trigger an update because it is still the same list (but with extra values in it).

With your new code you are changing the whole value of the StateFlow to a new list which triggers an update.

You can simplify your new code to something like:

autocompletePlaces.update { 
  it   response.autocompletePredictions.map { prediction ->
    prediction.getPrimaryText(null).toString()
  } 
}
  • Related