androidandroid-jetpack-composegoogle-placesplaceautocompletefragment

Implementing Google places autoComplete textfield implementation in jetpack compose android


Did anyone implement google autocomplete suggestion text field or fragment in a jetpack compose project? If so kindly guide or share code snippets as I'm having difficulty in implementing it.

Update

Here is the intent that I'm triggering to open full-screen dialog, but when I start typing within it gets closed, and also I'm unable to figure out what the issue is and need a clue about handling on activity result for reading the result of the predictions within this compose function.

    Places.initialize(context, "sa")

    val fields = listOf(Place.Field.ID, Place.Field.NAME)

    val intent = Autocomplete.IntentBuilder(
        AutocompleteActivityMode.FULLSCREEN,fields).build(context)

        startActivityForResult(context as MainActivity,intent, AUTOCOMPLETE_REQUEST_CODE, Bundle.EMPTY)
                            

Solution

  • I am using the MVVM architecture and this is how I implemented it:

    GooglePlacesApi

    I've created an api for reaching google api named GooglePlacesApi
    interface GooglePlacesApi {
        @GET("maps/api/place/autocomplete/json")
        suspend fun getPredictions(
            @Query("key") key: String = <GOOGLE_API_KEY>,
            @Query("types") types: String = "address",
            @Query("input") input: String
        ): GooglePredictionsResponse
    
        companion object{
            const val BASE_URL = "https://maps.googleapis.com/"
        }
    }
    

    The @Query("types") field is for specifiying what are you looking for in the query, you can look for establishments etc. Types can be found here

    Models

    So I created 3 models for this implementation:
    GooglePredictionsResponse
    The way the response looks if you are doing a GET request with postman is:

    Google Prediction Response

    You can see that we have an object with "predictions" key so this is our first model.

    data class GooglePredictionsResponse(
        val predictions: ArrayList<GooglePrediction>
    )
    
    GooglePredictionTerm
    data class GooglePredictionTerm(
        val offset: Int,
        val value: String
    )
    
    GooglePrediction
    data class GooglePrediction(
        val description: String,
        val terms: List<GooglePredictionTerm>
    )
    

    I only needed that information, if you need anything else, feel free to modify the models or create your own.

    GooglePlacesRepository

    And finally we create the repository to get the information (I'm using hilt to inject my dependencies, you can ignore those annotations if not using it)
    @ActivityScoped
    class GooglePlacesRepository @Inject constructor(
        private val api: GooglePlacesApi,
    ){
        suspend fun getPredictions(input: String): Resource<GooglePredictionsResponse>{
            val response = try {
                api.getPredictions(input = input)
            } catch (e: Exception) {
                Log.d("Rently", "Exception: ${e}")
                return Resource.Error("Failed prediction")
            }
    
            return Resource.Success(response)
        }
    }
    

    Here I've used an extra class I've created to handle the response, called Resource

    sealed class Resource<T>(val data: T? = null, val message: String? = null){
        class Success<T>(data: T): Resource<T>(data)
        class Error<T>(message: String, data:T? = null): Resource<T>(data = data, message = message)
        class Loading<T>(data: T? = null): Resource<T>(data = data)
    }
    

    View Model

    Again I'm using hilt so ignore annotations if not using it.
    @HiltViewModel
    class AddApartmentViewModel @Inject constructor(private val googleRepository: GooglePlacesRepository): ViewModel(){
    
        val isLoading = mutableStateOf(false)
        val predictions = mutableStateOf(ArrayList<GooglePrediction>())
        
        fun getPredictions(address: String) {
            viewModelScope.launch {
                isLoading.value = true
                val response = googleRepository.getPredictions(input = address)
                when(response){
                    is Resource.Success -> {
                        predictions.value = response.data?.predictions!!
                    }
                }
    
                isLoading.value = false
            }
        }
    
        fun onSearchAddressChange(address: String){
            getPredictions(address)
        }
    }
    

    If you need any further help let me know