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()
}
}