Learn how to find an address or place with a search bar and the geocoding service.
 
    
Geocoding is the process of converting address or place text into a location. The geocoding service can search for an address or a place and perform reverse geocoding.
In this tutorial, you use a search bar in the user interface to access the Geocoding service and search for addresses and places.
Prerequisites
Before starting this tutorial, you need the following:
- 
An ArcGIS Location Platform or ArcGIS Online account. 
- 
A development and deployment environment that meets the system requirements. 
- 
An IDE for Android development in Kotlin. 
Set up authentication
To access the secure ArcGIS location services used in this tutorial, you must implement API key authentication or user authentication using an ArcGIS Location Platform or an ArcGIS Online account.
You can implement API key authentication or user authentication in this tutorial. Compare the differences below:
API key authentication
- Users are not required to sign in.
- Requires creating an API key credential with the correct privileges.
- API keys are long-lived access tokens.
- Service usage is billed to the API key owner/developer.
- Simplest authentication method to implement.
- Recommended approach for new ArcGIS developers.
Learn more in API key authentication.
User authentication
- Users are required to sign in with an ArcGIS account.
- User accounts must have privilege to access the ArcGIS services used in application.
- Requires creating OAuth credentials.
- Application uses a redirect URL and client ID.
- Service usage is billed to the organization of the user signed into the application.
Learn more in User authentication.
Create a new API key access token with privileges to access the secure resources used in this tutorial.
- 
Complete the Create an API key tutorial and create an API key with the following privilege(s): - Privileges
- Location services > Basemaps
- Location services > Geocoding
 
 
- Privileges
- 
Copy and paste the API key access token into a safe location. It will be used in a later step. 
Develop or download
You have two options for completing this tutorial:
Option 1: Develop the code
Open an Android Studio project
- 
Open the project you created by completing the Display a map tutorial. 
- 
Continue with the following instructions to add a search bar to the user interface and search for an address or place using the ArcGIS Geocoding service. 
- 
Modify the old project for use in this new tutorial. - 
On your file system, delete the .idea folder, if present, at the top level of your project. 
- 
In the Android view, open app > res > values > strings.xml. In the <string name="appelement, change the text content to Search for an address._name" > strings.xmlUse dark colors for code blocks <resources> <string name="app_name">Search for an address</string> </resources>
- 
In the Android view, open Gradle Scripts > settings.gradle.kts. Change the value of rootto "Search for an address".Project.name settings.gradle.ktsUse dark colors for code blocks dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral() maven { url = uri("https://esri.jfrog.io/artifactory/arcgis") } } } rootProject.name = "Search for an address" include(":app")
- 
Click File > Sync Project with Gradle files. Android Studio will recognize your changes and create a new .idea folder. 
 
- 
Set developer credentials
If you implemented API key authentication in the Display a map tutorial, the API key access token will only have the Basemaps privilege. The Search for an address tutorial requires the Geocoding privilege to search for an address using the LocatorTask. To create an API Key access token that has the Basemaps and Geocoding privileges, see the Set up authentication step and then follow the instructions below.
- 
In the Android view of Android Studio, open app > kotlin+java > com.example.app > MainActivity. 
- 
In the onlifecycle method of theCreate() Mainclass, set theActivity ArcGISEnvironment.apiKeyproperty by callingApiKey.create(). Pass in your API key access token as a string and don't forget the double quotes. Do this before thesetblock.Content MainActivity.ktUse dark colors for code blocks override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ArcGISEnvironment.apiKey = ApiKey.create("YOUR_ACCESS_TOKEN")
Best Practice: The access token is stored directly in the code as a convenience for this tutorial. Do not store credentials directly in source code in a production environment.
Add import statements and some Compose variables
- 
Modify import statements to reference the packages and classes required for this tutorial. Theme.ktUse dark colors for code blocks @file:OptIn(ExperimentalMaterial3Api::class) package com.example.app.screens import android.content.Context import android.widget.Toast import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Search import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.Scaffold import androidx.compose.material3.SearchBar import androidx.compose.material3.SearchBarDefaults import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource import com.arcgismaps.Color import com.arcgismaps.geometry.SpatialReference import com.arcgismaps.mapping.ArcGISMap import com.arcgismaps.mapping.BasemapStyle import com.arcgismaps.mapping.Viewpoint import com.arcgismaps.mapping.symbology.HorizontalAlignment import com.arcgismaps.mapping.symbology.SimpleMarkerSymbol import com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyle import com.arcgismaps.mapping.symbology.TextSymbol import com.arcgismaps.mapping.symbology.VerticalAlignment import com.arcgismaps.mapping.view.Graphic import com.arcgismaps.mapping.view.GraphicsOverlay import com.arcgismaps.tasks.geocode.GeocodeParameters import com.arcgismaps.tasks.geocode.GeocodeResult import com.arcgismaps.tasks.geocode.LocatorTask import com.arcgismaps.toolkit.geoviewcompose.MapView import com.arcgismaps.toolkit.geoviewcompose.MapViewProxy import com.example.app.R import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.launch
- 
In the Maincomposable, create Compose variables that will be passed to various functions in theScreen Mainfile.Screen.kt These are remembered state variables and use either remember()orrememberCoroutine Scope() Briefly, these variables are as follows: - 
context: The local context of your app.
- 
coroutine: Set toScope remember. You will use this variable to launch a coroutine.Coroutine Scope() 
- 
focus: AManager Focuswill allow you to clear focus from the search bar, dismissing the device keyboard after a search is submitted.Manager 
- 
query: The address that the user enters in the search bar.Text 
- 
current: TheJob currentvariable is of typeJob Mutable, which references the Kotlin coroutineState <Job? > Job. (It does not reference the interfaceJobfrom ArcGIS Maps SDK for Kotlin.) Note thatcoroutinereturns a Kotlin coroutineScope.launch {} Job.
- 
graphics: AOverlay GraphicsOverlayto hold the text symbol (the search address) and a red square symbol (the found location on the map).
- 
graphics: A list ofOverlay GraphicsOverlay. You pass this list to theMapView.
- 
map: AView Proxy MapViewProxy, defined in ArcGIS Maps SDK for Kotlin Toolkit, allows you to set the view point on the map view.
- 
current: The currentSpatial Reference SpatialReference.
 Theme.ktUse dark colors for code blocks @Composable fun MainScreen() { val context = LocalContext.current val coroutineScope = rememberCoroutineScope() // The focus manager is used to dismiss keyboard after search query submission. val focusManager = LocalFocusManager.current var queryText by remember { mutableStateOf("") } val currentJob = remember { mutableStateOf<Job?>(null) } val graphicsOverlay = remember { GraphicsOverlay() } val graphicsOverlays = remember { listOf(graphicsOverlay) } val mapViewProxy = remember { MapViewProxy() } val currentSpatialReference = remember { mutableStateOf<SpatialReference?>(null) } val map = remember { createMap() } Scaffold( topBar = { TopAppBar(title = { Text(text = stringResource(id = R.string.app_name)) }) } ) { MapView( modifier = Modifier.fillMaxSize(), arcGISMap = map, ) } }
- 
Create a function to geocode an address
Geocoding is implemented with a locator, typically created by referencing a service such as the geocoding service or, for offline geocoding, by referencing locator data contained in a mobile package. Geocoding parameters can be used to refine the results, such as setting a maximum number of results or requesting additional attributes in the results.
- 
Define a top-level suspendfunction namedsearch. Declare the parameters shown below.Address() MainScreen.ktUse dark colors for code blocks suspend fun searchAddress( context: Context, coroutineScope: CoroutineScope, query: String, currentSpatialReference: SpatialReference?, graphicsOverlay: GraphicsOverlay, mapViewProxy: MapViewProxy ) { }
- 
Create a LocatorTaskbased on the Geocoding service.A locator task is used to find the location of an address (geocode) or to interpolate an address for a location (reverse geocode). An address includes any type of information that distinguishes a place. A locator involves finding matching locations for a given address. Reverse-geocoding is the opposite and finds the closest address for a given location. MainScreen.ktUse dark colors for code blocks suspend fun searchAddress( context: Context, coroutineScope: CoroutineScope, query: String, currentSpatialReference: SpatialReference?, graphicsOverlay: GraphicsOverlay, mapViewProxy: MapViewProxy ) { val geocodeServerUri = "https://geocode-api.arcgis.com/arcgis/rest/services/World/GeocodeServer" val locatorTask = LocatorTask(geocodeServerUri) }
- 
Create a new GeocodeParametersand define some of its properties.- Add the names of attributes to return to the GeocodeParameters.resultAttributeNamescollection. An asterisk (*) indicates all attributes.
- Set the maximum number of results to be returned with GeocodeParameters.maxResults. Results are ordered byscore, so that the first result has the best match score (ranging from 0 for no match to 100 for the best match).
- Set the spatial reference for result locations with GeocodeParameters.outputSpatialReference. By default, the output spatial reference is defined by the geocode service. For optimal performance when displaying the geocode result, you can ensure that returned coordinates match those of the map view by providing the map view's spatial reference.
 When geocoding an address, you can optionally provide GeocodeParametersto control certain aspects of the geocoding operation and specify the kinds of results to return from the locator task. Learn more about these parameters in theGeocodeParameters. For a list of attributes returned with geocode results, see Geocoding service output in the ArcGIS services reference.MainScreen.ktUse dark colors for code blocks suspend fun searchAddress( context: Context, coroutineScope: CoroutineScope, query: String, currentSpatialReference: SpatialReference?, graphicsOverlay: GraphicsOverlay, mapViewProxy: MapViewProxy ) { val geocodeServerUri = "https://geocode-api.arcgis.com/arcgis/rest/services/World/GeocodeServer" val locatorTask = LocatorTask(geocodeServerUri) // Create geocode parameters val geocodeParameters = GeocodeParameters().apply { resultAttributeNames.add("*") maxResults = 1 outputSpatialReference = currentSpatialReference } }
- Add the names of attributes to return to the 
- 
To find the location for the address that the user entered, call LocatorTask.geocode(), passing thequeryandgeocodevariables.Parameters In the .onblock, call theSuccess handlefunction (which we will define in a later section). Pass parameters toGeocode Results() handleas shown below.Geocode Results() The geocode results are implemented as a list of GeocodeResultobjects. In this tutorial, either one or zero results will be returned, as the maximum results parameter was set to 1.MainScreen.ktUse dark colors for code blocks suspend fun searchAddress( context: Context, coroutineScope: CoroutineScope, query: String, currentSpatialReference: SpatialReference?, graphicsOverlay: GraphicsOverlay, mapViewProxy: MapViewProxy ) { val geocodeServerUri = "https://geocode-api.arcgis.com/arcgis/rest/services/World/GeocodeServer" val locatorTask = LocatorTask(geocodeServerUri) // Create geocode parameters val geocodeParameters = GeocodeParameters().apply { resultAttributeNames.add("*") maxResults = 1 outputSpatialReference = currentSpatialReference } // Search for the address locatorTask.geocode(searchText = query, parameters = geocodeParameters) .onSuccess { geocodeResults: List<GeocodeResult> -> handleGeocodeResults( context, coroutineScope, geocodeResults, graphicsOverlay, mapViewProxy ) }.onFailure { error -> showMessage(context, "The locatorTask.geocode() call failed: ${error.message}") } }
Create two graphics
You will create a graphic that uses a text symbol to display the address as a string. You will create another graphic that uses a red square to indicate the location on the map.
- 
Define a top-level function named createand declare aText Graphic() GeocodeResultparameter. For thetextof theTextSymbol, use theGeocodeResult.labelproperty. For thegeometryof theGraphic, use theGeocodeResult.displayLocationproperty.MainScreen.ktUse dark colors for code blocks fun createTextGraphic(geocodeResult: GeocodeResult): Graphic { val textSymbol = TextSymbol( text = geocodeResult.label, color = Color.black, size = 18f, horizontalAlignment = HorizontalAlignment.Center, verticalAlignment = VerticalAlignment.Bottom ).apply { offsetY = 8f haloColor = Color.white haloWidth = 2f } return Graphic( geometry = geocodeResult.displayLocation, symbol = textSymbol ) }
- 
Define a top-level function named createand declare aMarker Graphic() GeocodeResultparameter. For thegeometryof theGraphic, use theGeocodeResult.labelproperty. For theattributesof theGraphic, use theGeocodeResult.attributesproperty.MainScreen.ktUse dark colors for code blocks fun createMarkerGraphic(geocodeResult: GeocodeResult): Graphic { val simpleMarkerSymbol = SimpleMarkerSymbol( style = SimpleMarkerSymbolStyle.Square, color = Color.red, size = 12.0f ) return Graphic( geometry = geocodeResult.displayLocation, attributes = geocodeResult.attributes, symbol = simpleMarkerSymbol ) }
Display the result
The result obtained from the geocode operation can be displayed by adding the two graphics you just created to the map view's graphics overlay.
- 
Define a suspendfunction namedhandle. Declare the parameters shown below.Geocode Results() MainScreen.ktUse dark colors for code blocks fun handleGeocodeResults( context: Context, coroutineScope: CoroutineScope, geocodeResults: List<GeocodeResult>, graphicsOverlay: GraphicsOverlay, mapViewProxy: MapViewProxy ) { }
- 
If the geocodelist is not empty, do the following: First, get the firstResults GeocodeResultfrom thegeocodelist. Next, callResults createandText Graphic() create, passing that geocode result to each. Last, add the two graphics to theMarker Graphic() GraphicsOverlay.MainScreen.ktUse dark colors for code blocks fun handleGeocodeResults( context: Context, coroutineScope: CoroutineScope, geocodeResults: List<GeocodeResult>, graphicsOverlay: GraphicsOverlay, mapViewProxy: MapViewProxy ) { if (geocodeResults.isNotEmpty()) { val geocodeResult = geocodeResults[0] // Create a Text graphic to display the address text, and add it to the graphics overlay. val textGraphic = createTextGraphic(geocodeResult) // Create a red square marker graphic, and add it to the graphics overlay. val markerGraphic = createMarkerGraphic(geocodeResult) // Clear previous results and add graphics. graphicsOverlay.graphics.apply { clear() add(textGraphic) add(markerGraphic) } }
- 
Inside a coroutineblock, get the display location (which is aScope.launch Point) from the geocode result and assign it to a variable namedcenter. Then callPoint MapViewProxy.setViewpointCenter(), which is defined in the ArcGIS Maps SDK for Kotlin Toolkit, and passcenter.Point Last, add code to handle the case where the geocodelist is empty.Results MainScreen.ktUse dark colors for code blocks fun handleGeocodeResults( context: Context, coroutineScope: CoroutineScope, geocodeResults: List<GeocodeResult>, graphicsOverlay: GraphicsOverlay, mapViewProxy: MapViewProxy ) { if (geocodeResults.isNotEmpty()) { val geocodeResult = geocodeResults[0] // Create a Text graphic to display the address text, and add it to the graphics overlay. val textGraphic = createTextGraphic(geocodeResult) // Create a red square marker graphic, and add it to the graphics overlay. val markerGraphic = createMarkerGraphic(geocodeResult) // Clear previous results and add graphics. graphicsOverlay.graphics.apply { clear() add(textGraphic) add(markerGraphic) } coroutineScope.launch { val centerPoint = geocodeResult.displayLocation ?: return@launch showMessage(context, "The locatorTask.geocode() call failed") // Animate the map view to the center point. mapViewProxy.setViewpointCenter(centerPoint) .onFailure { error -> showMessage(context, "Failed to set Viewpoint center: ${error.message}") } } } else { showMessage(context, "No address found for the given query") } }
Add a search bar and pass parameters to Map
To search an address using the application, add a search bar that will accept an address as text input.
Next, modify the Scaffold so it contains a Column. Inside the Column, you will add a Search, followed by MapView.
- 
Inside the Scaffoldblock, find theMapViewfrom the Display a map tutorial and replace it with a call ofColumn. AColumnallows you to display the search bar at the top of the screen and the map view directly below.MainScreen.ktUse dark colors for code blocks @Composable fun MainScreen() { val context = LocalContext.current val coroutineScope = rememberCoroutineScope() // The focus manager is used to dismiss keyboard after search query submission. val focusManager = LocalFocusManager.current var queryText by remember { mutableStateOf("") } val currentJob = remember { mutableStateOf<Job?>(null) } val graphicsOverlay = remember { GraphicsOverlay() } val graphicsOverlays = remember { listOf(graphicsOverlay) } val mapViewProxy = remember { MapViewProxy() } val currentSpatialReference = remember { mutableStateOf<SpatialReference?>(null) } val map = remember { createMap() } Scaffold( topBar = { TopAppBar(title = { Text(text = stringResource(id = R.string.app_name)) }) } ) { Column( modifier = Modifier .fillMaxSize() .padding(it) ) { } } }
- 
Call the Searchcomposable, passing the parameters shown below.Bar Pass a lambda for the onparameter. When theSearch oncallback is invoked, the lambda should clear the focus from the search bar (thus dismissing the device keyboard), then cancel any previous search job, and start a new search job by callingSearch search. Note that theAddress() onlambda provides a parameter with the value of the current input query, which you must pass toSearch search.Address() MainScreen.ktUse dark colors for code blocks @Composable fun MainScreen() { val context = LocalContext.current val coroutineScope = rememberCoroutineScope() // The focus manager is used to dismiss keyboard after search query submission. val focusManager = LocalFocusManager.current var queryText by remember { mutableStateOf("") } val currentJob = remember { mutableStateOf<Job?>(null) } val graphicsOverlay = remember { GraphicsOverlay() } val graphicsOverlays = remember { listOf(graphicsOverlay) } val mapViewProxy = remember { MapViewProxy() } val currentSpatialReference = remember { mutableStateOf<SpatialReference?>(null) } val map = remember { createMap() } Scaffold( topBar = { TopAppBar(title = { Text(text = stringResource(id = R.string.app_name)) }) } ) { Column( modifier = Modifier .fillMaxSize() .padding(it) ) { SearchBar( inputField = { SearchBarDefaults.InputField( query = queryText, onQueryChange = { query -> queryText = query }, onSearch = { currentQuery -> focusManager.clearFocus() // Cancel any previous search job. currentJob.value?.cancel() // Start a new search job. currentJob.value = coroutineScope.launch { searchAddress( context, coroutineScope, currentQuery, currentSpatialReference.value, graphicsOverlay, mapViewProxy ) } }, expanded = false, onExpandedChange = { }, enabled = true, placeholder = { Text("Search for an address") }, leadingIcon = { Icon(Icons.Filled.Search, contentDescription = "Search") }) }, expanded = false, onExpandedChange = {}, modifier = Modifier.fillMaxWidth(), ) {} } } }
- 
Add back the MapView, passing aModifierandmap.MainScreen.ktUse dark colors for code blocks Scaffold( topBar = { TopAppBar(title = { Text(text = stringResource(id = R.string.app_name)) }) } ) { Column( modifier = Modifier .fillMaxSize() .padding(it) ) { SearchBar( inputField = { SearchBarDefaults.InputField( query = queryText, onQueryChange = { query -> queryText = query }, onSearch = { currentQuery -> focusManager.clearFocus() // Cancel any previous search job. currentJob.value?.cancel() // Start a new search job. currentJob.value = coroutineScope.launch { searchAddress( context, coroutineScope, currentQuery, currentSpatialReference.value, graphicsOverlay, mapViewProxy ) } }, expanded = false, onExpandedChange = { }, enabled = true, placeholder = { Text("Search for an address") }, leadingIcon = { Icon(Icons.Filled.Search, contentDescription = "Search") }) }, expanded = false, onExpandedChange = {}, modifier = Modifier.fillMaxWidth(), ) {} MapView( modifier = Modifier.fillMaxSize(), arcGISMap = map, ) } }
- 
Pass these additional parameters to MapView:- graphics: Pass the- Overlays - graphicsvariable, defined at the top of the- Overlays - Mainblock.- Screen 
- map: Pass the- View - Proxy - mapvariable (of type- View - Proxy - MapViewProxy, defined at the top of the- Mainblock.- Screen 
- on: Pass a lambda that retrieves a spatial reference and assigns it to the- Spatial - Reference - Changed - currentvariable, defined at the top of the- Spatial - Reference - Mainblock.- Screen 
 MainScreen.ktUse dark colors for code blocks MapView( modifier = Modifier.fillMaxSize(), arcGISMap = map, graphicsOverlays = graphicsOverlays, mapViewProxy = mapViewProxy, onSpatialReferenceChanged = { spatialReference -> currentSpatialReference.value = spatialReference } )
- 
(Optional) The code in this tutorial calls a function to display messages to the user. One possible implementation of showis the following.Message() MainScreen.ktUse dark colors for code blocks fun showMessage(context: Context, message: String) { Toast.makeText(context, message, Toast.LENGTH_LONG).show() }
- 
Click Run > Run > app to run the app. 
You should see a search box above the map. Search for an address by entering an address and press the magnifying glass on the on the device keyboard. The result of the search should display on the map as a red square.
Alternatively, you can download the tutorial solution, as follows.
Option 2: Download the solution
- 
Click the Download solution link in the right-hand side of this page. 
- 
Unzip the file to a location on your machine. 
- 
Run Android Studio. 
- 
Go to File > Open.... Navigate to the solution folder and click Open. On Windows: If you are in the Welcome to Android Studio dialog, click Open and navigate to the solution folder. Then click Open. 
Since the downloaded solution does not contain authentication credentials, you must add the developer credentials that you created in the Set up authentication section.
Set developer credentials in the solution
To allow your app users to access ArcGIS location services, use the developer credentials that you created in the Set up authentication step to authenticate requests for resources.
- 
In the Android view of Android Studio, open app > kotlin+java > com.example.app > MainActivity. Set the AuthenticationtoMode ..API _KEY MainActivity.ktUse dark colors for code blocks class MainActivity : ComponentActivity() { private enum class AuthenticationMode { API_KEY, USER_AUTH } private val authenticationMode = AuthenticationMode.API_KEY
- 
Set the apiproperty with your API key access token.Key MainActivity.ktUse dark colors for code blocks override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) when (authenticationMode) { AuthenticationMode.API_KEY -> { ArcGISEnvironment.apiKey = ApiKey.create("YOUR_ACCESS_TOKEN") }
Best Practice: The access token is stored directly in the code as a convenience for this tutorial. Do not store credentials directly in source code in a production environment.
Run the app
Click Run > Run > app to run the app.
You should see a search box above the map. Search for an address by entering an address and press the magnifying glass on the on the device keyboard. The result of the search should display on the map as a red square.
What's next?
Learn how to use additional API features, ArcGIS location services, and ArcGIS tools in these tutorials: