> For the complete documentation index, see [llms.txt](https://docs.mapbox.com/help/llms.txt)

# Add Location Search to an Android app

This tutorial will walk you through how to add location search to your Android app using Mapbox's **Search SDK for Android**. This will allow a user to search for places, addresses and points of interest, and categories like restaurants, gas stations, etc. Your final app will include a search input field that provides autocomplete suggestions as users type, and displays selected locations on the map with markers.

### What we'll cover

-   Installing the **Search SDK for Android** and setting up your secret token
-   Adding location permissions and marker assets to your project
-   Creating a main activity with Maps SDK integration
-   Building a search input component using Jetpack Compose
-   Implementing autocomplete suggestions with a composable UI to display search results
-   Handling suggestion selection and map interactions
-   Adding markers to the map for selected locations
-   Enhancing search with proximity and bounding box parameters

Your browser doesn't support HTML5 video. Open [link to the video](https://docs.mapbox.com/help/help/assets/medias/tutorials--android-location-search--flyto-search-result-d93f507fca92f78d1c0342b942c333e9.mp4).

If you would like to run the finished product locally before following the tutorial, you can find the source code for this tutorial on [GitHub](https://github.com/mapbox/tutorials/).

Get ready to start building! This tutorial is designed to be completed in about 30 minutes.

## Prerequisites

To follow along with this guide you'll need:

-   **A Mapbox account**: [Sign up](https://account.mapbox.com/auth/signup/) or [login](https://console.mapbox.com/) to a free account
-   **Android Studio**: The latest version of [Android Studio](https://developer.android.com/studio/install) with Gradle
-   **A project with the Maps SDK installed**: Follow the [Getting Started with the Maps SDK for Android](https://docs.mapbox.com/android/maps/guides/install/) guide.
-   **Familiarity with Android development**: Beginner experience with Jetpack Compose, Kotlin, and Android Studio.

This tutorial assumes you have a basic understanding of Kotlin and Android development and requires you to have setup the **Mapbox Maps SDK for Android** for a Jetpack Compose project as covered in the [Getting Started with Mapbox on Android](https://docs.mapbox.com/android/maps/guides/install/) guide. That guide walks you through adding the **Mapbox Maps SDK for Android** dependency and adding your public access token to your project's `mapbox_access_token.xml` file.

While its not required to use the **Maps SDK for Android** to use the **Search SDK for Android** a common pattern is to display a map and show an interactive search, where the user can move the map and fly to searched locations. This has many different use cases, such as searching for nearby restaurants, gas stations, or other points of interest based on the user's current location or a specific area on the map.

The Getting Started guide should leave you with a new Compose project open in Android with a globe displayed.

![Screenshot of an Android app showing a globe.](https://docs.mapbox.com/help/assets/ideal-img/tutorials--android-geofencing-1.8df3c0d.480.png)

Your `MainActivity.kt` file should look like this:

```kotlin
package com.example.codelabsexample // replace with your package name

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.ui.Modifier
import com.mapbox.geojson.Point
import com.mapbox.maps.extension.compose.MapboxMap
import com.mapbox.maps.extension.compose.animation.viewport.rememberMapViewportState

public class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MapboxMap(
                Modifier.fillMaxSize(),
                mapViewportState = rememberMapViewportState {
                    setCameraOptions {
                        zoom(2.0)
                        center(Point.fromLngLat(-98.0, 39.5))
                        pitch(0.0)
                        bearing(0.0)
                    }
                },
            )
        }
    }
}
```

## Create your secret token & add the Search SDK

Before you can use location search in your Android app, you'll need to add the **Mapbox Search SDK** to your project. A secret token is required to download the **Mapbox Search SDK**.

### Create and configure your secret token

To create your secret token and add it to your `gradle.properties` file follow these steps:

1.  [Create a Secret Token](https://docs.mapbox.com/android/search/guides/install/#step-1-create-a-secret-token)
2.  [Configure your secret token](https://docs.mapbox.com/android/search/guides/install/#step-2-configure-your-secret-token)

Now that you have your secret token added to your `gradle.properties` file, add the following lines to your `settings.gradle.kts`. These lines reference the secret token you created, and will use it to authenticate the download of the Search SDK dependencies.

```kotlin
pluginManagement {
    repositories {
        google {
            content {
                includeGroupByRegex("com\\.android.*")
                includeGroupByRegex("com\\.google.*")
                includeGroupByRegex("androidx.*")
            }
        }
        mavenCentral()
        gradlePluginPortal()
    }
}
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
        // Mapbox Maven repository
        maven {
            url = uri("https://api.mapbox.com/downloads/v2/releases/maven")
            authentication {
                create<BasicAuthentication>("basic")
            }
            credentials {
                // Do not change the username below.
                // This should always be `mapbox` (not your username).
                username = "mapbox"
                // Use the secret token you stored in gradle.properties as the password
                password = providers.gradleProperty("MAPBOX_DOWNLOADS_TOKEN").get()
            }
        }
    }
}

rootProject.name = "Android Location Search"
include(":app")
 
```

### Add the Search SDK dependencies

2.  **Add the Search SDK dependency** to your app-level `build.gradle` file:

```gradle
dependencies {
    // highlight-start
    implementation("com.mapbox.search:mapbox-search-android:2.14.0-beta.1")
    // highlight-end
    implementation("com.mapbox.extension:maps-compose:11.13.3")
    implementation("com.mapbox.maps:android:11.13.3")
}
```

Next, you'll need to sync your gradle files with your project to download the new dependency.

> **Note: Mapbox Search SDK Dependencies & Installation**
> 
> The [**Search SDK for Android**](https://docs.mapbox.com/android/search/guides/install/) has a [complete install guide](https://docs.mapbox.com/android/search/guides/install/) available. The basic steps here have been followed, but you've only included the core search SDK dependency. You can find additional dependencies in the Search SDK documentation.

## Add required permissions & marker assets

### Location permissions

Your app needs location permissions to provide better search results based on the user's current location.

Add the following permissions to your `AndroidManifest.xml` file, if you don't already have them. This allows your app to access the user's location.

```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    // highlight-start
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    // highlight-end

    <application
        android:allowBackup="true"
        ...
        >
```

### Add marker assets to Drawables folder

First start by downloading and unzipping this `markers.zip` file.

[Download marker.zip](https://docs.mapbox.com/help/help/data/markers.zip)

This folder contains 2 icons, an `*.xml` Marker file, used in the search results list below, and a `*.png` marker file you will use to display the selected search result on the map in the next step.

Copy the `map_marker.png` and `search_result_marker.xml` files into your Android project under the `res/drawable` folder.

Next you'll create the main activity for your app.

## Create the main activity

The `MainActivity.kt` file will host both the map and the search interface. It manages the state between search results and map display & interactions.

Replace the contents of your `MainActivity.kt` file with the following code.

**Note:** you will need to change the first line of the file to match your package name.

```kotlin
package com.example.androidlocationsearch // Change this to your package name

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import com.mapbox.geojson.Point

import com.mapbox.maps.extension.compose.MapboxMap
import com.mapbox.maps.extension.compose.animation.viewport.rememberMapViewportState

@OptIn(ExperimentalComposeUiApi::class)

class MainActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            val mapViewportState = rememberMapViewportState {
                setCameraOptions {
                    center(Point.fromLngLat(-98.0, 39.5))
                    zoom(2.0)
                }
            }

            Box(Modifier.fillMaxSize()) {
                MapboxMap(
                    Modifier.fillMaxSize(),
                    mapViewportState = mapViewportState,
                    scaleBar = {}, // disables the scale bar by rendering nothing
                    compass = {} // disable the compass
                )
            }
        }
    }
}
```

This code moves the [`setCameraOptions`](https://docs.mapbox.com/android/maps/api/11.13.5/mapbox-maps-android/com.mapbox.maps.extension.compose.animation.viewport/-map-viewport-state/set-camera-options.html) declaration to the [`rememberMapViewportState`](https://docs.mapbox.com/android/maps/api/11.13.5/mapbox-maps-android/com.mapbox.maps.extension.compose.animation.viewport/remember-map-viewport-state.html) block, and adds a `Box` wrapper around the [`MapboxMap`](https://docs.mapbox.com/android/maps/api/11.13.5/mapbox-maps-android/com.mapbox.maps/-mapbox-map/) to allow for additional UI elements to be added on top of the map.

Next you'll create the search component.

## Add the Search Input

The search screen component handles the user interface for location search, including the input field, autocomplete suggestions, and result selection.

> **Note (warning): Search SDK UI Components**
> 
> The **Search SDK for Android** provides a set of UI components for building search interfaces, including input fields, suggestion lists, and result displays in the Android Views UI paradigm. You can learn more about these components in the [Search SDK for Android documentation](https://docs.mapbox.com/android/search/guides/).
> 
> This tutorial covers building a custom search UI using Jetpack Compose and composable functions.

Create a new file called `SearchScreen.kt` and add the following code:

```kotlin
package com.example.androidlocationsearch

import android.util.Log
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.mapbox.maps.dsl.cameraOptions
import com.mapbox.maps.extension.compose.animation.viewport.MapViewportState
import com.mapbox.maps.plugin.animation.MapAnimationOptions
import com.mapbox.search.ApiType
import com.mapbox.search.ResponseInfo
import com.mapbox.search.SearchEngine
import com.mapbox.search.SearchEngineSettings
import com.mapbox.search.SearchOptions
import com.mapbox.search.SearchSelectionCallback
import com.mapbox.search.SearchSuggestionsCallback
import com.mapbox.search.result.SearchResult
import com.mapbox.search.result.SearchSuggestion


@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SearchScreen(
    modifier: Modifier = Modifier,
) {
    var query by remember { mutableStateOf("") }
    var suggestions by remember { mutableStateOf<List<SearchSuggestion>>(emptyList()) }

    // Get SearchEngine instance
    val searchEngine = remember {
        SearchEngine.createSearchEngine(
            ApiType.SEARCH_BOX, // Use Search Box API
            SearchEngineSettings()
        )
    }

    Column(
        modifier = modifier
            .fillMaxWidth()
            .padding(16.dp)
    ) {
        // SearchInput UI
        OutlinedTextField(
            value = query,
            onValueChange = { newQuery ->
                query = newQuery
                if (newQuery.length >= 2) {
                    searchEngine.search(
                        newQuery,
                        SearchOptions(
                            limit = 10 // Limit to 10 suggestions
                        ),
                        callback = object : SearchSuggestionsCallback {
                            override fun onSuggestions(
                                list: List<SearchSuggestion>,
                                responseInfo: ResponseInfo
                            ) {
                                list.forEachIndexed { index, suggestion ->
                                  Log.d("Suggestion[$index]", """
                                      ID: ${suggestion.id}
                                      Name: ${suggestion.name}
                                      Description: ${suggestion.descriptionText ?: "N/A"}
                                      Place: ${suggestion.address?.place ?: "N/A"}
                                      Region: ${suggestion.address?.region ?: "N/A"}
                                      Country: ${suggestion.address?.country ?: "N/A"}
                                      Type: ${suggestion.type ?: "N/A"}
                                      -----
                                  """.trimIndent())
                              }
                            }

                            override fun onError(e: Exception) {
                                Log.e("SearchScreen", "Search error", e)
                            }
                        }
                    )
                } else {
                    suggestions = emptyList()
                }
            },

            label = { Text("Search") },
            singleLine = true,
            modifier = Modifier
                .fillMaxWidth()
                .padding(8.dp),
            colors = TextFieldDefaults.outlinedTextFieldColors(
                containerColor = Color.White,          // White background
                focusedBorderColor = Color.Gray,       // Grey border when focused
                unfocusedBorderColor = Color.LightGray // Grey border when unfocused (lighter)
            ),
            shape = RoundedCornerShape(8.dp)
        )
    }
}
```

### Understanding the `SearchScreen` component

This `SearchScreen.kt` component establishes the foundation for search functionality in the app. It creates a [`SearchEngine`](https://docs.mapbox.com/android/search/api/core/2.14.0-beta.1/sdk/com.mapbox.search/-search-engine/) instance using [`SearchEngine.createSearchEngine()`](https://docs.mapbox.com/android/search/api/core/2.14.0-beta.1/sdk/com.mapbox.search/-search-engine/-companion/create-search-engine.html) with `ApiType.SEARCH_BOX` to access Mapbox's [Search Box API](https://docs.mapbox.com/api/search/search-box/) and is configured with basic [`SearchEngineSettings()`](https://docs.mapbox.com/android/search/api/core/2.14.0-beta.1/sdk/com.mapbox.search/-search-engine-settings/). The component manages two key state variables: `query` tracks the current search text, while `suggestions` will store the list of search results. At present, these results are not yet displayed in the UI.

The search functionality triggers when users type 2 or more characters, calling [`searchEngine.search()`](https://docs.mapbox.com/android/search/api/core/2.14.0-beta.1/sdk/com.mapbox.search/-search-engine/search.html) with `SearchOptions(limit = 10)` to restrict results to 10 suggestions maximum. The search response is handled through a [`SearchSuggestionsCallback`](https://docs.mapbox.com/android/search/api/core/2.14.0-beta.1/sdk/com.mapbox.search/-search-suggestions-callback/) and logs suggestions to the console. The user interface centers around an [`OutlinedTextField`](https://developer.android.com/reference/kotlin/androidx/compose/material3/package-summary#OutlinedTextField) with Material UI 3 styling that updates the query state as users type.

Details

**Deep Dive: Search Box API's Suggest & Retrieve Endpoints**

The [Search Box API](https://docs.mapbox.com/api/search/search-box/) which is defined in the `apiType` in the `createSearchEngine` method above, provides multiple endpoints for searching and retrieving location data. In this tutorial you use Search Box's `suggest` and `retrieve` endpoints, which when used together provide an interactive 2 stage search experience. First the `suggest` endpoint is called and shows search suggestions based on the input as a user types, and secondly, once the user clicks a suggestion the `retrieve` endpoint is called, returning the full information on the selected result.

For more information on this 2 stage search, see the [Search Box API documentation](https://docs.mapbox.com/api/search/search-box/#interactive-search).

The **Search SDK for Android** used in this tutorial provides a convenient way to access these endpoints through the `SearchEngine` class, allowing you to implement location search functionality with minimal setup.

### Import `SearchScreen.kt` into `MainActivity.kt`

Now that you have created the `SearchScreen.kt` file, you can integrate it into your `MainActivity.kt` to display the search interface on top of the map.

Open your `MainActivity.kt` file and change the `setContent` block to include the `SearchScreen` composable. You'll also need to add 2 imports at the top of the file.

```kotlin

package com.example.androidlocationsearch // Change this to your package name

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import com.mapbox.geojson.Point
import androidx.compose.ui.Alignment
import androidx.compose.ui.zIndex

import com.mapbox.maps.extension.compose.MapboxMap
import com.mapbox.maps.extension.compose.animation.viewport.rememberMapViewportState

@OptIn(ExperimentalComposeUiApi::class)

class MainActivity : ComponentActivity() {

  override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)

      setContent {
          val mapViewportState = rememberMapViewportState {
              setCameraOptions {
                  center(Point.fromLngLat(-98.0, 39.5))
                  zoom(2.0)
              }
          }

          Box(Modifier.fillMaxSize()) {
              MapboxMap(
                  Modifier.fillMaxSize(),
                  mapViewportState = mapViewportState,
                  scaleBar = {}, // disables the scale bar by rendering nothing
                  compass = {} // disable the compass
              )

              SearchScreen(
                  modifier = Modifier
                      .align(Alignment.TopCenter)
                      .zIndex(1f)
              )
          }
      }
  }
}
```

Now when you run your app, you should see the search input field displayed on top of the map. If you use the input to search for an address, location or Point of Interest, you can see the suggestions logged in the console.

Your browser doesn't support HTML5 video. Open [link to the video](https://docs.mapbox.com/help/help/assets/medias/tutorials--android-location-search--logging-suggestions-f7a961df26be26c3f5557617f3c6e497.mp4).

In the next steps, you will enhance this component to display the suggestions list and handle user selection to interact with the map.

## Build Suggestions UI

Now that you have the search functionality set up, you will build a composable UI to display the search suggestions and allow users to select a suggestion.

### Update `SearchScreen.kt` to render suggestions

Open your `SearchScreen.kt` file and add the following highlighted code to build the suggestions UI.

```kotlin
package com.example.androidlocationsearch

import android.util.Log
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.foundation.clickable
import androidx.compose.foundation.rememberScrollState
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.Alignment
import androidx.compose.ui.unit.sp
import androidx.compose.ui.unit.dp
import com.mapbox.search.result.SearchSuggestionType.Category
import androidx.compose.ui.Modifier
import com.mapbox.search.ApiType
import com.mapbox.search.ResponseInfo
import com.mapbox.search.SearchEngine
import com.mapbox.search.SearchEngineSettings
import com.mapbox.search.SearchOptions
import com.mapbox.search.SearchSuggestionsCallback
import com.mapbox.search.result.SearchSuggestion


@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SearchScreen(
    modifier: Modifier = Modifier,
) {
    var query by remember { mutableStateOf("") }
    var suggestions by remember { mutableStateOf<List<SearchSuggestion>>(emptyList()) }

    // Get SearchEngine instance
    val searchEngine = remember {
        SearchEngine.createSearchEngine(
            ApiType.SEARCH_BOX,
            SearchEngineSettings()
        )
    }

    Column(
        modifier = modifier
            .fillMaxWidth()
            .padding(16.dp)
    ) {
        // Inline SearchInput UI
        OutlinedTextField(
            value = query,
            onValueChange = { newQuery ->
                query = newQuery
                if (newQuery.length >= 2) {
                    searchEngine.search(
                        newQuery,
                        SearchOptions(
                            limit = 10,
                        ),
                        callback = object : SearchSuggestionsCallback {
                            override fun onSuggestions(
                                list: List<SearchSuggestion>,
                                responseInfo: ResponseInfo
                            ) {
                                // remove logging and pass list array to the suggestions var
                                suggestions = list
                            }

                            override fun onError(e: Exception) {
                                Log.e("SearchScreen", "Search error", e)
                            }
                        }
                    )
                } else {
                    suggestions = emptyList()
                }
            },

            label = { Text("Search") },
            singleLine = true,
            modifier = Modifier
                .fillMaxWidth()
                .padding(8.dp),
            colors = OutlinedTextFieldDefaults.colors(
                focusedContainerColor = Color.White,          // White background
                unfocusedContainerColor = Color.White,
                focusedBorderColor = Color.Gray,       // Grey border when focused
                unfocusedBorderColor = Color.LightGray // Grey border when unfocused (lighter)
            ),
            shape = RoundedCornerShape(8.dp)
        )

        Spacer(modifier = Modifier.height(16.dp))

        // Suggestions list anchored right below the TextField
        if (suggestions.isNotEmpty()) {
            Surface(
                color = Color.White,
                shadowElevation = 4.dp,
                shape = RoundedCornerShape(8.dp),
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(top=8.dp, start=16.dp, end=16.dp)
                    .heightIn(max = 500.dp)
            ) {
                val scrollState = rememberScrollState()
                Column(
                    modifier = Modifier
                        .padding(16.dp)
                        .verticalScroll(scrollState)
                ) {
                    // Filter out category suggestion types
                    val filteredSuggestions = suggestions.filter { it.type !is Category }

                    filteredSuggestions.forEachIndexed { index, suggestion ->

                        // Get distance in kilometers if possible
                        val distanceKm = suggestion.distanceMeters?.div(1000.0)
                        val addressText = suggestion.fullAddress
                            ?: listOfNotNull(
                                suggestion.address?.region,
                                suggestion.address?.country
                            ).joinToString(", ")

                        Row(
                            modifier = Modifier
                                .fillMaxWidth()
                                .clickable {
                                    suggestions = emptyList()
                                    // Our click handler for selecting a 
                                    // suggestion will go here in the next step
                                }
                                .padding(vertical = 12.dp, horizontal = 8.dp),
                            verticalAlignment = Alignment.CenterVertically
                        ) { 
                            Icon(
                                painter = painterResource(id = R.drawable.search_result_marker),
                                contentDescription = "Mapbox Marker",
                                tint = Color.Unspecified,
                                modifier = Modifier
                                    .size(24.dp)
                            )

                            Column(
                                modifier = Modifier.padding(start = 16.dp)
                            ) {
                                Text(
                                    text = suggestion.name,
                                    fontWeight = FontWeight.Bold,
                                    fontSize = 16.sp
                                )

                                Text(
                                    text = addressText,
                                    fontSize = 14.sp,
                                    color = Color.Gray,
                                    modifier = Modifier.padding(top = 4.dp)
                                )

                                distanceKm?.let { km ->
                                    Text(
                                        text = String.format("%.1f km", km),
                                        fontSize = 12.sp,
                                        color = Color.Gray,
                                        modifier = Modifier.padding(top = 4.dp)
                                    )
                                }

                            }
                        }
                        if (index < suggestions.lastIndex) {
                            HorizontalDivider(color = Color.LightGray)
                        }
                    }

                }
            }
        }
    }
}
```

This code updates the file with needed imports, removes the logging of suggestions and instead passes the suggestions `list[]` to the `suggestions` variable and adds the UI to display the suggestions.

### Rendering the Suggestions list

Below the `OutlinedTextField`, a `Spacer` is added and then a `Surface` component is added to display the suggestions list. Inside the `Surface`, a `Column` is added and loop over the `suggestions` list with a `forEachIndexed()` to display a `Row` element for each suggestion. The `Row` includes a function to format distances to kilometers, displays a marker `Icon` which references the drawable resource you added earlier and displays the suggestion `name` and `fullAddress`.

The end result of the code above is a suggestions list that appears below the search input field when users type a query. Each suggestion displays an icon marker, the name, full address and distance (if available).

![Screenshot of an Android app showing search suggestions below the input field.](https://docs.mapbox.com/help/assets/ideal-img/tutorials--android-location-search--suggestions-ui.626d6e3.480.png)

Next, you'll implement the functionality to handle user selection of a suggestion and flying the map to the selected location.

## Handle suggestion selection and map interaction

Now that you are rendering suggestions, you need to handle user selection and update the map to add a marker at the selected location and fly the map to it.

### Update `MainActivity.kt`

First you'll update `MainActivity.kt` to add the needed imports, add a state variable to hold the `selectedResult`, and a function within `MapboxMap` to add a `PointAnnotation` (marker) to the map at the selected result's coordinates. And finally, you'll update the `SearchScreen` composable to pass down the `mapViewportState` and the `onSuggestionSelected` callback.

Update your `MainActivity.kt` file with the highlighted code updates below:

```kotlin
package com.example.androidlocationsearch // replace with your package name

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.zIndex
import com.mapbox.geojson.Point
import com.mapbox.maps.extension.compose.MapboxMap
import com.mapbox.maps.extension.compose.animation.viewport.rememberMapViewportState
// highlight-start
import androidx.compose.runtime.*
import androidx.compose.ui.res.painterResource
import com.mapbox.maps.extension.compose.annotation.generated.PointAnnotation
import com.mapbox.maps.extension.compose.annotation.rememberIconImage
import com.mapbox.search.result.SearchResult
// highlight-end

@OptIn(ExperimentalComposeUiApi::class)

class MainActivity : ComponentActivity() {


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            // highlight-start
            val selectedResult = remember { mutableStateOf<SearchResult?>(null) }
            // highlight-end
            val mapViewportState = rememberMapViewportState {
                setCameraOptions {
                    center(Point.fromLngLat(-98.0, 39.5))
                    zoom(2.0)
                }
            }

            Box(Modifier.fillMaxSize()) {
                MapboxMap(
                    Modifier.fillMaxSize(),
                    mapViewportState = mapViewportState,
                    scaleBar = {}, // disables the scale bar by rendering nothing
                    compass = {} // disable the compass
                ) {
                    // highlight-start
                    selectedResult.value?.coordinate?.let { coord ->
                        val marker = rememberIconImage(
                            key = R.drawable.map_marker,
                            painter = painterResource(id = R.drawable.map_marker)
                        )
                        PointAnnotation(point = coord) {
                            iconImage = marker
                        }
                    }
                    // highlight-end
                }

                SearchScreen(
                    // highlight-start
                    mapViewportState = mapViewportState,
                    onSuggestionSelected = { result ->
                        selectedResult.value = result
                    },
                    // highlight-end
                    modifier = Modifier
                        .align(Alignment.TopCenter)
                        .zIndex(1f)
                )
            }
        }
    }
}
```

### Update `SearchScreen.kt`

Next, you'll update the `SearchScreen.kt` file to accept the `mapViewportState` and `onSuggestionSelected` parameters, and you'll create a new function to handle the selection of a suggestion.

```kotlin
package com.example.androidlocationsearch

import android.util.Log
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.mapbox.maps.extension.compose.animation.viewport.MapViewportState
// Update imports
import com.mapbox.maps.plugin.animation.MapAnimationOptions
import com.mapbox.maps.dsl.cameraOptions
import com.mapbox.search.SearchSelectionCallback
import com.mapbox.search.ApiType
import com.mapbox.search.ResponseInfo
import com.mapbox.search.SearchEngine
import com.mapbox.search.SearchEngineSettings
import com.mapbox.search.SearchOptions
import com.mapbox.search.SearchSuggestionsCallback
import com.mapbox.search.result.SearchResult
import com.mapbox.search.result.SearchSuggestion
import com.mapbox.search.result.SearchSuggestionType.Category


@OptIn(ExperimentalMaterial3Api::class)
@Composable
// Update function parameters
fun SearchScreen(
    modifier: Modifier = Modifier,
    mapViewportState: MapViewportState,
    onSuggestionSelected: (SearchResult) -> Unit
) {
    var query by remember { mutableStateOf("") }
    var suggestions by remember { mutableStateOf<List<SearchSuggestion>>(emptyList()) }

    // Get SearchEngine instance
    val searchEngine = remember {
        SearchEngine.createSearchEngine(
            ApiType.SEARCH_BOX,
            SearchEngineSettings()
        )
    }

    Column(
        modifier = modifier
            .fillMaxWidth()
            .padding(16.dp)
    ) {
        // Inline SearchInput UI
        OutlinedTextField(
            value = query,
            onValueChange = { newQuery ->
                query = newQuery
                if (newQuery.length >= 2) {
                    searchEngine.search(
                        newQuery,
                        SearchOptions(
                            limit = 10
                        ),
                        callback = object : SearchSuggestionsCallback {
                            override fun onSuggestions(
                                list: List<SearchSuggestion>,
                                responseInfo: ResponseInfo
                            ) {
                                suggestions = list
                            }

                            override fun onError(e: Exception) {
                                Log.e("SearchScreen", "Search error", e)
                            }
                        }
                    )
                } else {
                    suggestions = emptyList()
                }
            },

            label = { Text("Search") },
            singleLine = true,
            modifier = Modifier
                .fillMaxWidth()
                .padding(8.dp),
            colors = OutlinedTextFieldDefaults.colors(
                focusedContainerColor = Color.White,          // White background
                unfocusedContainerColor = Color.White,
                focusedBorderColor = Color.Gray,       // Grey border when focused
                unfocusedBorderColor = Color.LightGray // Grey border when unfocused (lighter)
            ),
            shape = RoundedCornerShape(8.dp)
        )


        Spacer(modifier = Modifier.height(16.dp))

        // Suggestions list anchored right below the TextField
        if (suggestions.isNotEmpty()) {
            Surface(
                color = Color.White,
                shadowElevation = 4.dp,
                shape = RoundedCornerShape(8.dp),
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(top=8.dp, start=16.dp, end=16.dp)
                    .heightIn(max = 500.dp)
            ) {
                val scrollState = rememberScrollState()
                Column(
                    modifier = Modifier
                        .padding(16.dp)
                        .verticalScroll(scrollState)
                ) {
                    // Filter out category suggestion types
                    val filteredSuggestions = suggestions.filter { it.type !is Category }

                    filteredSuggestions.forEachIndexed { index, suggestion ->

                        val distanceKm = suggestion.distanceMeters?.div(1000.0)
                        val addressText = suggestion.fullAddress
                            ?: listOfNotNull(
                                suggestion.address?.region,
                                suggestion.address?.country
                            ).joinToString(", ")

                        Row(
                            modifier = Modifier
                                .fillMaxWidth()
                                // Add click handler to suggestion element
                                .clickable {
                                    suggestions = emptyList()
                                    handleSuggestionSelection(suggestion, searchEngine, mapViewportState, onSuggestionSelected)
                                }
                                .padding(vertical = 12.dp, horizontal = 8.dp),
                            verticalAlignment = Alignment.CenterVertically
                        ) {
                            Icon(
                                painter = painterResource(id = R.drawable.search_result_marker),
                                contentDescription = "Mapbox Marker",
                                tint = Color.Unspecified, // Use this if your vector defines its own fill color
                                modifier = Modifier
                                    .size(24.dp)
                            )

                            Column(
                                modifier = Modifier.padding(start = 16.dp)
                            ) {
                                Text(
                                    text = suggestion.name,
                                    fontWeight = FontWeight.Bold,
                                    fontSize = 16.sp
                                )

                                Text(
                                    text = addressText,
                                    fontSize = 14.sp,
                                    color = Color.Gray,
                                    modifier = Modifier.padding(top = 4.dp)
                                )

                                distanceKm?.let { km ->
                                    Text(
                                        text = String.format("%.1f km", km),
                                        fontSize = 12.sp,
                                        color = Color.Gray,
                                        modifier = Modifier.padding(top = 4.dp)
                                    )
                                }

                            }
                        }
                        if (index < suggestions.lastIndex) {
                            HorizontalDivider(color = Color.LightGray)
                        }
                    }

                }
            }
        }
    }
}

// Function to handle Suggestion click
fun handleSuggestionSelection(
    suggestion: SearchSuggestion,
    searchEngine: SearchEngine,
    mapViewportState: MapViewportState,
    onSuggestionSelected: (SearchResult) -> Unit

) {
    searchEngine.select(suggestion, object: SearchSelectionCallback {
        override fun onResult(
            suggestion: SearchSuggestion,
            result: SearchResult,
            responseInfo: ResponseInfo
        ) {
            // When user selects a suggestion:
            onSuggestionSelected(result)
            val coordinate = result.coordinate
            // Set the center of the cameraOptions to the result coordinate
            val camera = cameraOptions {
                center(coordinate)
                zoom(14.0)
            }

            val animationOptions = MapAnimationOptions.Builder()
                .duration(3000L) // 3 seconds
                .build()

            // Fly the map to the result location
            mapViewportState.flyTo(camera, animationOptions)
        }

        override fun onResults(
            suggestion: SearchSuggestion,
            results: List<SearchResult>,
            responseInfo: ResponseInfo
        ) {
            // handle multiple results (category, brand, etc.)
        }


        override fun onSuggestions(
            suggestions: List<SearchSuggestion>,
            responseInfo: ResponseInfo
        ) {
            // override if needed
        }

        override fun onError(e: Exception) {
            Log.e("Search", "Selection error", e)
        }
    })
}
```

Now when a user selects a suggestion from the list, the `onSuggestionSelected` callback is triggered, passing the selected `SearchResult` to the `MainActivity.kt`. The `MainActivity.kt` then updates the `selectedResult` state variable, which in turn adds a marker to the map at the selected location and animates the map to that location.

Your browser doesn't support HTML5 video. Open [link to the video](https://docs.mapbox.com/help/help/assets/medias/tutorials--android-location-search--flyto-search-result-d93f507fca92f78d1c0342b942c333e9.mp4).

## Enhance search with proximity and bounding box

Now that you have basic search functionality in place, you can enhance the search experience by providing more relevant results based on the user's location or a specific geographic area.

### Bias results around a point with `proximity`

To bias search results toward a specific location, you can use the `proximity` parameter in your `search Options` object. This parameter takes a `Point` representing the desired location and will bias search results to favor locations near this point.

In the `SearchScreen.kt` file, locate the `SearchOptions` configuration in the search engine call. You can update it to include a `proximity` parameter. In the code below we've added a proximity to the Distillery District in Toronto, Canada.

```kotlin
package com.example.androidlocationsearch

import android.util.Log
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
// Add imports for geojson Point & BoundingBox
// import com.mapbox.geojson.Point
// import com.mapbox.geojson.BoundingBox
import com.mapbox.maps.dsl.cameraOptions
import com.mapbox.maps.extension.compose.animation.viewport.MapViewportState
import com.mapbox.maps.plugin.animation.MapAnimationOptions
import com.mapbox.search.ApiType
import com.mapbox.search.ResponseInfo
import com.mapbox.search.SearchEngine
import com.mapbox.search.SearchEngineSettings
import com.mapbox.search.SearchOptions
import com.mapbox.search.SearchSuggestionsCallback
import com.mapbox.search.SearchSelectionCallback
import com.mapbox.search.result.SearchResult
import com.mapbox.search.result.SearchSuggestion
import com.mapbox.search.result.SearchSuggestionType.Category



@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SearchScreen(
    mapViewportState: MapViewportState,
    modifier: Modifier = Modifier,
    onSuggestionSelected: (SearchResult) -> Unit
) {
    var query by remember { mutableStateOf("") }
    var suggestions by remember { mutableStateOf<List<SearchSuggestion>>(emptyList()) }

    // Get SearchEngine instance
    val searchEngine = remember {
        SearchEngine.createSearchEngine(
            ApiType.SEARCH_BOX,
            SearchEngineSettings()
        )
    }

    Column(
        modifier = modifier
            .fillMaxWidth()
            .padding(16.dp)
    ) {
        // Inline SearchInput UI
        OutlinedTextField(
            value = query,
            onValueChange = { newQuery ->
                query = newQuery
                if (newQuery.length >= 2) {
                    searchEngine.search(
                        newQuery,
                        SearchOptions(
                            // proximity = Point.fromLngLat(-79.35954, 43.65050),
                            limit = 10,
                            // boundingBox = BoundingBox.fromPoints(
                               // Point.fromLngLat(-79.49555, 43.60698), 
                               // Point.fromLngLat(-79.29422, 43.75953)  
                            // )
                        ),
                        callback = object : SearchSuggestionsCallback {
                            override fun onSuggestions(
                                list: List<SearchSuggestion>,
                                responseInfo: ResponseInfo
                            ) {
                                suggestions = list
                            }

                            override fun onError(e: Exception) {
                                Log.e("SearchScreen", "Search error", e)
                            }
                        }
                    )
                } else {
                    suggestions = emptyList()
                }
            },

            label = { Text("Search") },
            singleLine = true,
            modifier = Modifier
                .fillMaxWidth()
                .padding(8.dp),
            colors = OutlinedTextFieldDefaults.colors(
                focusedContainerColor = Color.White,          // White background
                unfocusedContainerColor = Color.White,
                focusedBorderColor = Color.Gray,       // Grey border when focused
                unfocusedBorderColor = Color.LightGray // Grey border when unfocused (lighter)
            ),
            shape = RoundedCornerShape(8.dp)
        )

        Spacer(modifier = Modifier.height(16.dp))

        // Suggestions list anchored right below the TextField
        if (suggestions.isNotEmpty()) {
            Surface(
                color = Color.White,
                shadowElevation = 4.dp,
                shape = RoundedCornerShape(8.dp),
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(top = 8.dp)
                    .heightIn(max = 500.dp)
            ) {
                val scrollState = rememberScrollState()
                Column(
                    modifier = Modifier
                        .padding(16.dp)
                        .verticalScroll(scrollState)
                ) {

                    // Filter out category suggestion types
                    val filteredSuggestions = suggestions.filter { it.type !is Category }

                    filteredSuggestions.forEachIndexed { index, suggestion ->

                        val distanceKm = suggestion.distanceMeters?.div(1000.0)
                        val addressText = suggestion.fullAddress
                            ?: listOfNotNull(suggestion.address?.region, suggestion.address?.country).joinToString(", ")

                        Row(
                            modifier = Modifier
                                .fillMaxWidth()
                                .clickable {
                                    suggestions = emptyList()
                                    handleSuggestionSelection(suggestion, searchEngine, mapViewportState, onSuggestionSelected)
                                }
                                .padding(vertical = 12.dp, horizontal = 8.dp),
                            verticalAlignment = Alignment.CenterVertically
                        ) {
                            Icon(
                                painter = painterResource(id = R.drawable.search_result_marker),
                                contentDescription = "Mapbox Marker",
                                tint = Color.Unspecified, // Use this if your vector defines its own fill color
                                modifier = Modifier
                                    .size(24.dp)
                            )

                            Column(
                                modifier = Modifier.padding(start = 16.dp)
                            ) {
                                Text(
                                    text = suggestion.name,
                                    fontWeight = FontWeight.Bold,
                                    fontSize = 16.sp
                                )

                                Text(
                                    text = addressText,
                                    fontSize = 14.sp,
                                    color = Color.Gray,
                                    modifier = Modifier.padding(top = 4.dp)
                                )

                                distanceKm?.let { km ->
                                    Text(
                                        text = String.format("%.1f km", km),
                                        fontSize = 12.sp,
                                        color = Color.Gray,
                                        modifier = Modifier.padding(top = 4.dp)
                                    )
                                }

                            }
                        }
                        if (index < suggestions.lastIndex) {
                            HorizontalDivider(color = Color.LightGray)
                        }
                    }

                }
            }
        }
    }
}

fun handleSuggestionSelection(
    suggestion: SearchSuggestion,
    searchEngine: SearchEngine,
    mapViewportState: MapViewportState,
    onSuggestionSelected: (SearchResult) -> Unit

) {
    searchEngine.select(suggestion, object: SearchSelectionCallback {
        override fun onResult(
            suggestion: SearchSuggestion,
            result: SearchResult,
            responseInfo: ResponseInfo
        ) {
            // When user selects a suggestion:
            onSuggestionSelected(result)
            val coordinate = result.coordinate

            val camera = cameraOptions {
                center(coordinate)
                zoom(14.0)
            }

            val animationOptions = MapAnimationOptions.Builder()
                .duration(3000L) // 3 seconds
                .build()

            mapViewportState.flyTo(camera, animationOptions)

        }

        override fun onResults(
            suggestion: SearchSuggestion,
            results: List<SearchResult>,
            responseInfo: ResponseInfo
        ) {
            // handle multiple results (category, brand, etc.)
        }


        override fun onSuggestions(
            suggestions: List<SearchSuggestion>,
            responseInfo: ResponseInfo
        ) {
            // override if needed
        }

        override fun onError(e: Exception) {
            Log.e("Search", "Selection error", e)
        }
    })
}
```

Restart your emulator, and now when you search `Coffee Shops` in the app, the results will be biased towards the Distillery District in Toronto, Canada.

![Screenshot of an Android app showing search results biased towards a specific location.](https://docs.mapbox.com/help/assets/ideal-img/tutorials--android-location-search--proximity-bias.9e77def.480.png)

### Restrict results to a geographic area with `boundingBox`

Sometimes it's desirable to restrict search results to a specific geographic area. For instance, in a local search app for a small business association. While `proximity` parameter biases results toward a specific location, the `boundingBox` parameter restricts results to a geographic area.

Add the `boundingBox` parameter to your `SearchOptions` in the `searchEngine.search()` call. This parameter takes a `BoundingBox` defined by two points: the southwest and northeast corners of the bounding box.

```kotlin
// Inside the searchEngine.search() call
SearchOptions(
    proximity = Point.fromLngLat(-79.35954, 43.65050), // Proximity to Toronto's Distillery District
    limit = 10,
    boundingBox = BoundingBox.fromPoints(
        Point.fromLngLat(-79.49555, 43.60698), // Southwest corner of GTA
        Point.fromLngLat(-79.29422, 43.75953)  // Northeast corner of GTA
    ) // Bounding Box of the Greater Toronto Area
)
```

These coordinates were acquired using Mapbox's [Location Helper](https://labs.mapbox.com/location-helper/) tool, which allows you to find coordinates for any location. The bounding box can be visualized as a rectangle on the map, and search results will only include locations within this rectangle. You can learn more about [Bounding Boxes](https://docs.mapbox.com/help/help/glossary/bounding-box/) in the glossary.

![Screenshot of bounding box on the map.](https://docs.mapbox.com/help/assets/ideal-img/tutorials--android-location-search--bounding-box.2a03e56.480.png)

After restarting your emulator, you can now find and locate different locations and POI's in the Greater Toronto Area. Try searching 'CN Tower', 'Horseshoe Tavern' or '317 Dundas Street West'. But when you try to search for 'Richmond Hill', a prominent neighborhood just north of Toronto or '1 Canada's Wonderland Drive', the address of a popular theme park outside the bounding box, you see that they are not returned in the results.

## Final Product

🎉 **Congratulations!** You have successfully built a location search feature for your Android app using the **Mapbox Search SDK** and **Maps SDK for Android**.

Below is the final code from the `MainActivity.kt` and `SearchScreen.kt` files. You can compare your code to the final code to make sure everything is working correctly.

**MainActivity.kt**

```kotlin
package com.example.androidlocationsearch

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.zIndex
import com.mapbox.geojson.Point
import com.mapbox.maps.extension.compose.MapboxMap
import com.mapbox.maps.extension.compose.animation.viewport.rememberMapViewportState
import com.mapbox.maps.extension.compose.annotation.generated.PointAnnotation
import com.mapbox.maps.extension.compose.annotation.rememberIconImage
import com.mapbox.search.result.SearchResult

@OptIn(ExperimentalComposeUiApi::class)

class MainActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            val selectedResult = remember { mutableStateOf<SearchResult?>(null) }
            val mapViewportState = rememberMapViewportState {
                setCameraOptions {
                    center(Point.fromLngLat(-98.0, 39.5))
                    zoom(2.0)
                }
            }

            Box(Modifier.fillMaxSize()) {
                MapboxMap(
                    Modifier.fillMaxSize(),
                    mapViewportState = mapViewportState,
                    scaleBar = {}, // disables the scale bar by rendering nothing
                    compass = {} // disable the compass
                ) {

                    selectedResult.value?.coordinate?.let { coord ->
                        val marker = rememberIconImage(
                            key = R.drawable.map_marker,
                            painter = painterResource(id = R.drawable.map_marker)
                        )
                        PointAnnotation(point = coord) {
                            iconImage = marker
                        }
                    }
                }

                SearchScreen(
                    mapViewportState = mapViewportState,
                    onSuggestionSelected = { result ->
                        selectedResult.value = result
                    },
                    modifier = Modifier
                        .align(Alignment.TopCenter)
                        .zIndex(1f)
                )
            }
        }
    }
}
```

**SearchScreen.kt**

```kotlin
package com.example.androidlocationsearch

import android.util.Log
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
// Add imports for geojson Point & BoundingBox
// import com.mapbox.geojson.Point
// import com.mapbox.geojson.BoundingBox
import com.mapbox.maps.dsl.cameraOptions
import com.mapbox.maps.extension.compose.animation.viewport.MapViewportState
import com.mapbox.maps.plugin.animation.MapAnimationOptions
import com.mapbox.search.ApiType
import com.mapbox.search.ResponseInfo
import com.mapbox.search.SearchEngine
import com.mapbox.search.SearchEngineSettings
import com.mapbox.search.SearchOptions
import com.mapbox.search.SearchSuggestionsCallback
import com.mapbox.search.SearchSelectionCallback
import com.mapbox.search.result.SearchResult
import com.mapbox.search.result.SearchSuggestion
import com.mapbox.search.result.SearchSuggestionType.Category



@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SearchScreen(
    mapViewportState: MapViewportState,
    modifier: Modifier = Modifier,
    onSuggestionSelected: (SearchResult) -> Unit
) {
    var query by remember { mutableStateOf("") }
    var suggestions by remember { mutableStateOf<List<SearchSuggestion>>(emptyList()) }

    // Get SearchEngine instance
    val searchEngine = remember {
        SearchEngine.createSearchEngine(
            ApiType.SEARCH_BOX,
            SearchEngineSettings()
        )
    }

    Column(
        modifier = modifier
            .fillMaxWidth()
            .padding(16.dp)
    ) {
        // Inline SearchInput UI
        OutlinedTextField(
            value = query,
            onValueChange = { newQuery ->
                query = newQuery
                if (newQuery.length >= 2) {
                    searchEngine.search(
                        newQuery,
                        SearchOptions(
                            // proximity = Point.fromLngLat(-79.35954, 43.65050),
                            limit = 10,
                            // boundingBox = BoundingBox.fromPoints(
                               // Point.fromLngLat(-79.49555, 43.60698), 
                               // Point.fromLngLat(-79.29422, 43.75953)  
                            // )
                        ),
                        callback = object : SearchSuggestionsCallback {
                            override fun onSuggestions(
                                list: List<SearchSuggestion>,
                                responseInfo: ResponseInfo
                            ) {
                                suggestions = list
                            }

                            override fun onError(e: Exception) {
                                Log.e("SearchScreen", "Search error", e)
                            }
                        }
                    )
                } else {
                    suggestions = emptyList()
                }
            },

            label = { Text("Search") },
            singleLine = true,
            modifier = Modifier
                .fillMaxWidth()
                .padding(8.dp),
            colors = OutlinedTextFieldDefaults.colors(
                focusedContainerColor = Color.White,          // White background
                unfocusedContainerColor = Color.White,
                focusedBorderColor = Color.Gray,       // Grey border when focused
                unfocusedBorderColor = Color.LightGray // Grey border when unfocused (lighter)
            ),
            shape = RoundedCornerShape(8.dp)
        )

        Spacer(modifier = Modifier.height(16.dp))

        // Suggestions list anchored right below the TextField
        if (suggestions.isNotEmpty()) {
            Surface(
                color = Color.White,
                shadowElevation = 4.dp,
                shape = RoundedCornerShape(8.dp),
                modifier = Modifier
                    .fillMaxWidth()
                    .padding(top = 8.dp)
                    .heightIn(max = 500.dp)
            ) {
                val scrollState = rememberScrollState()
                Column(
                    modifier = Modifier
                        .padding(16.dp)
                        .verticalScroll(scrollState)
                ) {

                    // Filter out category suggestion types
                    val filteredSuggestions = suggestions.filter { it.type !is Category }

                    filteredSuggestions.forEachIndexed { index, suggestion ->

                        val distanceKm = suggestion.distanceMeters?.div(1000.0)
                        val addressText = suggestion.fullAddress
                            ?: listOfNotNull(suggestion.address?.region, suggestion.address?.country).joinToString(", ")

                        Row(
                            modifier = Modifier
                                .fillMaxWidth()
                                .clickable {
                                    suggestions = emptyList()
                                    handleSuggestionSelection(suggestion, searchEngine, mapViewportState, onSuggestionSelected)
                                }
                                .padding(vertical = 12.dp, horizontal = 8.dp),
                            verticalAlignment = Alignment.CenterVertically
                        ) {
                            Icon(
                                painter = painterResource(id = R.drawable.search_result_marker),
                                contentDescription = "Mapbox Marker",
                                tint = Color.Unspecified, // Use this if your vector defines its own fill color
                                modifier = Modifier
                                    .size(24.dp)
                            )

                            Column(
                                modifier = Modifier.padding(start = 16.dp)
                            ) {
                                Text(
                                    text = suggestion.name,
                                    fontWeight = FontWeight.Bold,
                                    fontSize = 16.sp
                                )

                                Text(
                                    text = addressText,
                                    fontSize = 14.sp,
                                    color = Color.Gray,
                                    modifier = Modifier.padding(top = 4.dp)
                                )

                                distanceKm?.let { km ->
                                    Text(
                                        text = String.format("%.1f km", km),
                                        fontSize = 12.sp,
                                        color = Color.Gray,
                                        modifier = Modifier.padding(top = 4.dp)
                                    )
                                }

                            }
                        }
                        if (index < suggestions.lastIndex) {
                            HorizontalDivider(color = Color.LightGray)
                        }
                    }

                }
            }
        }
    }
}

fun handleSuggestionSelection(
    suggestion: SearchSuggestion,
    searchEngine: SearchEngine,
    mapViewportState: MapViewportState,
    onSuggestionSelected: (SearchResult) -> Unit

) {
    searchEngine.select(suggestion, object: SearchSelectionCallback {
        override fun onResult(
            suggestion: SearchSuggestion,
            result: SearchResult,
            responseInfo: ResponseInfo
        ) {
            // When user selects a suggestion:
            onSuggestionSelected(result)
            val coordinate = result.coordinate

            val camera = cameraOptions {
                center(coordinate)
                zoom(14.0)
            }

            val animationOptions = MapAnimationOptions.Builder()
                .duration(3000L) // 3 seconds
                .build()

            mapViewportState.flyTo(camera, animationOptions)

        }

        override fun onResults(
            suggestion: SearchSuggestion,
            results: List<SearchResult>,
            responseInfo: ResponseInfo
        ) {
            // handle multiple results (category, brand, etc.)
        }


        override fun onSuggestions(
            suggestions: List<SearchSuggestion>,
            responseInfo: ResponseInfo
        ) {
            // override if needed
        }

        override fun onError(e: Exception) {
            Log.e("Search", "Selection error", e)
        }
    })
}
```

> **Related content (related): [Download the final code](https://github.com/mapbox/tutorials/tree/main/android-location-search)**
> 
> The full source code for this tutorial is available on GitHub. You can download the final code and run it in Android Studio to see the complete location search functionality in action.

## Next steps

Now that you've successfully integrated location search into an Android app, here is a review of what you accomplished and some ideas for further enhancements:

### What we covered

-   Installing the **Search SDK for Android** and setting up your secret token
-   Adding location permissions and marker assets to your project
-   Creating a main activity with Maps SDK integration
-   Building a search input component using Jetpack Compose
-   Implementing autocomplete suggestions with a composable UI to display search results
-   Handling suggestion selection and map interactions
-   Adding markers to the map for selected locations
-   Enhancing search with proximity and bounding box parameters

### Further customization

With the basic location search functionality in place, you can enhance your app by:

#### Customize search interactivity to use search categories (restaurants, gas stations, etc.).

If you inspect the code inside the `SearchScreen.kt` file, you'll notice this line of code inside the `Surface` component that renders the suggestions list:

```kotlin
 // Filter out category suggestion types
 val filteredSuggestions = suggestions.filter { it.type !is Category }
```

This filters out suggestions that are of type `Category`, which are returned by the Search Box API. A search suggestion of type `Category` is a category of places, such as "Restaurants" or "Gas Stations" and is an array of `Results` rather than a single result that we've used in this tutorial. You can remove this filter to include category suggestions in your search results. Then in your `handleSuggestionSelection` function, you can handle the multiple results returned by the category suggestion in the `onResults` callback.

From there you could add a marker and a [`ViewAnnotation`](https://docs.mapbox.com/android/maps/api/11.13.5/mapbox-maps-android/com.mapbox.maps.extension.compose.annotation/-view-annotation.html) to display the name of each result next to the marker and move the map to re-center to map to view all the results.

#### Additional features to consider:

-   **Implementing search history**: Store and display recent searches
-   **Custom marker styling**: Create branded markers that match your app's design
-   **Offline search**: Implement offline search capabilities for better user experience

### Learn more

> **Related content (related): [Search SDK for Android documentation](https://docs.mapbox.com/android/search/guides/)**
> 
> Explore advanced features and customization options for the Mapbox Search SDK.

> **Related content (related): [Maps SDK for Android documentation](https://docs.mapbox.com/android/maps/guides/)**
> 
> Learn more about map customization, styling, and advanced features.