Learn how to find an address or place with a search bar and the geocoding service A geocoding service is a service that can search for addresses, place addresses, businesses, reverse geocode coordinates to addresses, provide suggestions for places, and perform bulk geocoding. It is hosted by Esri as the ArcGIS Geocoding service and can also be hosted in ArcGIS Enterprise. Learn more .

search for an address

Geocoding is the process of converting address or place A place, also known as a point of interest (POI), is a location that represents a business, administrative entity, or geographic feature around the world. A place can also have attributes associated with it, such as a name, address, category, and ID. Learn more text into a location A location is a position or region (point, line, or polygon) on the earth's surface. Learn more . The geocoding service A geocoding service is a service that can search for addresses, place addresses, businesses, reverse geocode coordinates to addresses, provide suggestions for places, and perform bulk geocoding. It is hosted by Esri as the ArcGIS Geocoding service and can also be hosted in ArcGIS Enterprise. Learn more can search for an address or a place and perform reverse geocoding Reverse geocoding is the process of converting a point to its nearest address or place. Learn more .

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:

  1. An ArcGIS Location Platform or ArcGIS Online account.

  2. A development and deployment environment that meets the system requirements.

  3. An IDE for Android development in Kotlin.

Set up authentication

To access the secure ArcGIS location services ArcGIS Location Services, also referred to as Location Services, are services hosted by Esri that provide geospatial functionality for developing mapping applications. They include the ArcGIS Basemap Styles service, ArcGIS Static Basemap Tiles service, ArcGIS Places service, ArcGIS Geocoding service, ArcGIS Routing service, ArcGIS GeoEnrichment service, and ArcGIS Elevation service. An ArcGIS Location Platform or ArcGIS Online account is required to use the services. Learn more used in this tutorial, you must implement API key authentication API key authentication is a type of authentication that uses an API key to authenticate requests to ArcGIS services and secure portal items. Learn more or user authentication User authentication is a type of authentication that allows users with an ArcGIS account to sign into an application and allow it to access ArcGIS content, services, and resources on their behalf. The typical authorization protocol used is OAuth2.0. Learn more using an ArcGIS Location Platform An ArcGIS Location Platform account, formerly known as an ArcGIS Developer account, is an identity associated with an ArcGIS Location Platform subscription. Learn more or an ArcGIS Online An ArcGIS Online account, also known as an ArcGIS Organization account, is an identity associated with an ArcGIS Online subscription. It can be used to access ArcGIS tools and develop applications with ArcGIS location services for an organization. Learn more account.

To complete this tutorial, click on the tab in the switcher below for your authentication type of choice, either API key authentication or User authentication.

Create a new API key access token An access token is an authorization string that provides access to secure ArcGIS content, data, and services. Its capabilities are determined by the privileges it supports. It is obtained by implementing API key authentication, User authentication, or App authentication. Learn more with privileges Privileges are a set of permissions assigned to ArcGIS accounts, developer credentials, and applications that grant access to secure resources and functionality in ArcGIS. Learn more to access the secure resources used in this tutorial.

  1. Complete the Create an API key tutorial and create an API key with the following privilege(s) Privileges are a set of permissions assigned to ArcGIS accounts, developer credentials, and applications that grant access to secure resources and functionality in ArcGIS. Learn more :

    • Privileges
      • Location services > Basemaps
      • Location services > Geocoding
  2. 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:

  1. Option 1: Develop the code or
  2. Option 2: Download the completed solution

Option 1: Develop the code

Open an Android Studio project

  1. Open the project you created by completing the Display a map tutorial.

  2. 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 A geocoding service is a service that can search for addresses, place addresses, businesses, reverse geocode coordinates to addresses, provide suggestions for places, and perform bulk geocoding. It is hosted by Esri as the ArcGIS Geocoding service and can also be hosted in ArcGIS Enterprise. Learn more .

  3. Modify the old project for use in this new tutorial.

Set developer credentials

If you implemented API key authentication API key authentication is a type of authentication that uses an API key to authenticate requests to ArcGIS services and secure portal items. Learn more 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.

  1. In the Android view of Android Studio, open app > kotlin+java > com.example.app > MainActivity.

  2. In the onCreate() lifecycle method of the MainActivity class, set the ArcGISEnvironment.apiKey​ property by calling ApiKey.create(). Pass in your API key access token as a string and don’t forget the double quotes. Do this before the setContent block.

    MainActivity.kt
    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

  1. Modify import statements to reference the packages and classes required for this tutorial.

    Theme.kt
    @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
  2. In the MainScreen composable, create Compose variables that will be passed to various functions in the MainScreen.kt file.

    Theme.kt
    @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 A locator is an ArcGIS dataset that stores address information and the rules for translating descriptions of places (such as street addresses or place names) into spatial data that can be displayed on a map. Learn more , typically created by referencing a service such as the geocoding service A geocoding service is a service that can search for addresses, place addresses, businesses, reverse geocode coordinates to addresses, provide suggestions for places, and perform bulk geocoding. It is hosted by Esri as the ArcGIS Geocoding service and can also be hosted in ArcGIS Enterprise. Learn more or, for offline Offline is the state of having no network connection and applications cannot access ArcGIS Online or ArcGIS Enterprise. Learn more geocoding, by referencing locator data contained in a mobile package A mobile package is a stand-alone Mobile Map Package (MMPK) or Mobile Scene Package (MSPK) file for use in offline applications built with ArcGIS Maps SDKs for Native Apps. Learn more . Geocoding parameters can be used to refine the results, such as setting a maximum number of results or requesting additional attributes in the results.

  1. Define a top-level suspend function named searchAddress(). Declare the parameters shown below.

    MainScreen.kt
    93 collapsed lines
    @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
    @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,
    )
    }
    }
    fun createMap(): ArcGISMap {
    return ArcGISMap(BasemapStyle.ArcGISTopographic).apply {
    initialViewpoint = Viewpoint(
    latitude = 34.0270,
    longitude = -118.8050,
    scale = 72000.0
    )
    }
    }
    suspend fun searchAddress(
    context: Context,
    coroutineScope: CoroutineScope,
    query: String,
    currentSpatialReference: SpatialReference?,
    graphicsOverlay: GraphicsOverlay,
    mapViewProxy: MapViewProxy
    ) {
    }
    4 collapsed lines
    fun showMessage(context: Context, message: String) {
    Toast.makeText(context, message, Toast.LENGTH_LONG).show()
    }
  2. Create a LocatorTask based on the Geocoding service.

    MainScreen.kt
    93 collapsed lines
    @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
    @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,
    )
    }
    }
    fun createMap(): ArcGISMap {
    return ArcGISMap(BasemapStyle.ArcGISTopographic).apply {
    initialViewpoint = Viewpoint(
    latitude = 34.0270,
    longitude = -118.8050,
    scale = 72000.0
    )
    }
    }
    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)
    }
    4 collapsed lines
    fun showMessage(context: Context, message: String) {
    Toast.makeText(context, message, Toast.LENGTH_LONG).show()
    }
  3. Create a new GeocodeParameters and define some of its properties.

    • Add the names of attributes to return to the GeocodeParameters.resultAttributeNames collection. An asterisk (*) indicates all attributes.
    • Set the maximum number of results to be returned with GeocodeParameters.maxResults. Results are ordered by score, 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 A spatial reference is a set of parameters, typically defined by a WKID, that define the coordinate system and spatial properties for geographic data. Applications use a spatial reference to correctly display the position of geographic data in a map or scene. Learn more 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.
    MainScreen.kt
    93 collapsed lines
    @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
    @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,
    )
    }
    }
    fun createMap(): ArcGISMap {
    return ArcGISMap(BasemapStyle.ArcGISTopographic).apply {
    initialViewpoint = Viewpoint(
    latitude = 34.0270,
    longitude = -118.8050,
    scale = 72000.0
    )
    }
    }
    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
    }
    }
    4 collapsed lines
    fun showMessage(context: Context, message: String) {
    Toast.makeText(context, message, Toast.LENGTH_LONG).show()
    }
  4. To find the location for the address that the user entered, call LocatorTask.geocode(), passing the query and geocodeParameters variables.

    In the .onSuccess block, call the handleGeocodeResults() function (which we will define in a later section). Pass parameters to handleGeocodeResults() as shown below.

    The geocode results are implemented as a list of GeocodeResult objects. In this tutorial, either one or zero results will be returned, as the maximum results parameter was set to 1.

    MainScreen.kt
    93 collapsed lines
    @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
    @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,
    )
    }
    }
    fun createMap(): ArcGISMap {
    return ArcGISMap(BasemapStyle.ArcGISTopographic).apply {
    initialViewpoint = Viewpoint(
    latitude = 34.0270,
    longitude = -118.8050,
    scale = 72000.0
    )
    }
    }
    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}")
    }
    }
    4 collapsed lines
    fun showMessage(context: Context, message: String) {
    Toast.makeText(context, message, Toast.LENGTH_LONG).show()
    }

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.

  1. Define a top-level function named createTextGraphic() and declare a GeocodeResult parameter. For the text of the TextSymbol, use the GeocodeResult.label property. For the geometry of the Graphic, use the GeocodeResult.displayLocation property.

    MainScreen.kt
    124 collapsed lines
    @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
    @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,
    )
    }
    }
    fun createMap(): ArcGISMap {
    return ArcGISMap(BasemapStyle.ArcGISTopographic).apply {
    initialViewpoint = Viewpoint(
    latitude = 34.0270,
    longitude = -118.8050,
    scale = 72000.0
    )
    }
    }
    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}")
    }
    }
    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
    )
    }
    4 collapsed lines
    fun showMessage(context: Context, message: String) {
    Toast.makeText(context, message, Toast.LENGTH_LONG).show()
    }
  2. Define a top-level function named createMarkerGraphic() and declare a GeocodeResult parameter. For the geometry of the Graphic, use the GeocodeResult.label property. For the attributes of the Graphic, use the GeocodeResult.attributes property.

    MainScreen.kt
    142 collapsed lines
    @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
    @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,
    )
    }
    }
    fun createMap(): ArcGISMap {
    return ArcGISMap(BasemapStyle.ArcGISTopographic).apply {
    initialViewpoint = Viewpoint(
    latitude = 34.0270,
    longitude = -118.8050,
    scale = 72000.0
    )
    }
    }
    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}")
    }
    }
    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
    )
    }
    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
    )
    }
    4 collapsed lines
    fun showMessage(context: Context, message: String) {
    Toast.makeText(context, message, Toast.LENGTH_LONG).show()
    }

Display the result

The result obtained from the geocode operation can be displayed by adding the two graphics A graphic is a visual element composed of a geometry, symbol, and attributes that is displayed on a map or scene. Learn more you just created to the map view’s graphics overlay A graphics overlay is a client-side, temporary container of graphics to display on a map view or scene view. Learn more .

  1. Define a suspend function named handleGeocodeResults(). Declare the parameters shown below.

    MainScreen.kt
    124 collapsed lines
    @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
    @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,
    )
    }
    }
    fun createMap(): ArcGISMap {
    return ArcGISMap(BasemapStyle.ArcGISTopographic).apply {
    initialViewpoint = Viewpoint(
    latitude = 34.0270,
    longitude = -118.8050,
    scale = 72000.0
    )
    }
    }
    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}")
    }
    }
    fun handleGeocodeResults(
    context: Context,
    coroutineScope: CoroutineScope,
    geocodeResults: List<GeocodeResult>,
    graphicsOverlay: GraphicsOverlay,
    mapViewProxy: MapViewProxy
    ) {
    }
    35 collapsed lines
    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
    )
    }
    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
    )
    }
    fun showMessage(context: Context, message: String) {
    Toast.makeText(context, message, Toast.LENGTH_LONG).show()
    }
  2. If the geocodeResults list is not empty, do the following: First, get the first GeocodeResult from the geocodeResults list. Next, call createTextGraphic() and createMarkerGraphic(), passing that geocode result to each. Last, add the two graphics to the GraphicsOverlay.

    MainScreen.kt
    124 collapsed lines
    @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
    @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,
    )
    }
    }
    fun createMap(): ArcGISMap {
    return ArcGISMap(BasemapStyle.ArcGISTopographic).apply {
    initialViewpoint = Viewpoint(
    latitude = 34.0270,
    longitude = -118.8050,
    scale = 72000.0
    )
    }
    }
    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}")
    }
    }
    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)
    }
    }
    35 collapsed lines
    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
    )
    }
    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
    )
    }
    fun showMessage(context: Context, message: String) {
    Toast.makeText(context, message, Toast.LENGTH_LONG).show()
    }
  3. Inside a coroutineScope.launch block, get the display location (which is a Point) from the geocode result and assign it to a variable named centerPoint. Then call MapViewProxy.setViewpointCenter(), which is defined in the ArcGIS Maps SDK for Kotlin Toolkit, and pass centerPoint.

    Last, add code to handle the case where the geocodeResults list is empty.

    MainScreen.kt
    124 collapsed lines
    @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
    @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,
    )
    }
    }
    fun createMap(): ArcGISMap {
    return ArcGISMap(BasemapStyle.ArcGISTopographic).apply {
    initialViewpoint = Viewpoint(
    latitude = 34.0270,
    longitude = -118.8050,
    scale = 72000.0
    )
    }
    }
    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}")
    }
    }
    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")
    }
    }
    35 collapsed lines
    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
    )
    }
    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
    )
    }
    fun showMessage(context: Context, message: String) {
    Toast.makeText(context, message, Toast.LENGTH_LONG).show()
    }

Add a search bar and pass parameters to MapView

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 SearchBar, followed by MapView.

  1. Inside the Scaffold block, find the MapView from the Display a map tutorial and replace it with a call of Column. A Column allows you to display the search bar at the top of the screen and the map view directly below.

    MainScreen.kt
    51 collapsed lines
    @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
    @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)
    ) {
    }
    }
    }
    115 collapsed lines
    fun createMap(): ArcGISMap {
    return ArcGISMap(BasemapStyle.ArcGISTopographic).apply {
    initialViewpoint = Viewpoint(
    latitude = 34.0270,
    longitude = -118.8050,
    scale = 72000.0
    )
    }
    }
    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}")
    }
    }
    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")
    }
    }
    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
    )
    }
    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
    )
    }
    fun showMessage(context: Context, message: String) {
    Toast.makeText(context, message, Toast.LENGTH_LONG).show()
    }
  2. Call the SearchBar composable, passing the parameters shown below.

    MainScreen.kt
    51 collapsed lines
    @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
    @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(),
    ) {}
    }
    }
    }
    115 collapsed lines
    fun createMap(): ArcGISMap {
    return ArcGISMap(BasemapStyle.ArcGISTopographic).apply {
    initialViewpoint = Viewpoint(
    latitude = 34.0270,
    longitude = -118.8050,
    scale = 72000.0
    )
    }
    }
    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}")
    }
    }
    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")
    }
    }
    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
    )
    }
    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
    )
    }
    fun showMessage(context: Context, message: String) {
    Toast.makeText(context, message, Toast.LENGTH_LONG).show()
    }
  3. Add back the MapView, passing a Modifier and map.

    MainScreen.kt
    69 collapsed lines
    @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
    @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(),
    ) {}
    MapView(
    modifier = Modifier.fillMaxSize(),
    arcGISMap = map,
    )
    }
    }
    117 collapsed lines
    }
    fun createMap(): ArcGISMap {
    return ArcGISMap(BasemapStyle.ArcGISTopographic).apply {
    initialViewpoint = Viewpoint(
    latitude = 34.0270,
    longitude = -118.8050,
    scale = 72000.0
    )
    }
    }
    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}")
    }
    }
    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")
    }
    }
    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
    )
    }
    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
    )
    }
    fun showMessage(context: Context, message: String) {
    Toast.makeText(context, message, Toast.LENGTH_LONG).show()
    }
  4. Pass these additional parameters to MapView:

    • graphicsOverlays: Pass the graphicsOverlays variable, defined at the top of the MainScreen block.
    • mapViewProxy: Pass the mapViewProxy variable (of type MapViewProxy, defined at the top of the MainScreen block.
    • onSpatialReferenceChanged: Pass a lambda that retrieves a spatial reference and assigns it to the currentSpatialReference variable, defined at the top of the MainScreen block.
    MainScreen.kt
    113 collapsed lines
    @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
    @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(),
    ) {}
    MapView(
    modifier = Modifier.fillMaxSize(),
    arcGISMap = map,
    graphicsOverlays = graphicsOverlays,
    mapViewProxy = mapViewProxy,
    onSpatialReferenceChanged = { spatialReference ->
    currentSpatialReference.value = spatialReference
    }
    )
    121 collapsed lines
    }
    }
    }
    fun createMap(): ArcGISMap {
    return ArcGISMap(BasemapStyle.ArcGISTopographic).apply {
    initialViewpoint = Viewpoint(
    latitude = 34.0270,
    longitude = -118.8050,
    scale = 72000.0
    )
    }
    }
    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}")
    }
    }
    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")
    }
    }
    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
    )
    }
    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
    )
    }
    fun showMessage(context: Context, message: String) {
    Toast.makeText(context, message, Toast.LENGTH_LONG).show()
    }
  5. (Optional) The code in this tutorial calls a function to display messages to the user. One possible implementation of showMessage() is the following.

    MainScreen.kt
    242 collapsed lines
    @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
    @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(),
    ) {}
    MapView(
    modifier = Modifier.fillMaxSize(),
    arcGISMap = map,
    graphicsOverlays = graphicsOverlays,
    mapViewProxy = mapViewProxy,
    onSpatialReferenceChanged = { spatialReference ->
    currentSpatialReference.value = spatialReference
    }
    )
    }
    }
    }
    fun createMap(): ArcGISMap {
    return ArcGISMap(BasemapStyle.ArcGISTopographic).apply {
    initialViewpoint = Viewpoint(
    latitude = 34.0270,
    longitude = -118.8050,
    scale = 72000.0
    )
    }
    }
    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}")
    }
    }
    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")
    }
    }
    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
    )
    }
    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
    )
    }
    fun showMessage(context: Context, message: String) {
    Toast.makeText(context, message, Toast.LENGTH_LONG).show()
    }
  6. 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

  1. Click the Download solution link in the right-hand side of this page.

  2. Unzip the file to a location on your machine.

  3. Run Android Studio.

  4. 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 ArcGIS Location Services, also referred to as Location Services, are services hosted by Esri that provide geospatial functionality for developing mapping applications. They include the ArcGIS Basemap Styles service, ArcGIS Static Basemap Tiles service, ArcGIS Places service, ArcGIS Geocoding service, ArcGIS Routing service, ArcGIS GeoEnrichment service, and ArcGIS Elevation service. An ArcGIS Location Platform or ArcGIS Online account is required to use the services. Learn more , use the developer credentials that you created in the Set up authentication step to authenticate requests for resources.

  1. In the Android view of Android Studio, open app > kotlin+java > com.example.app > MainActivity. Set the AuthenticationMode to .API_KEY.

    MainActivity.kt
    14 collapsed lines
    package com.example.app
    import android.os.Bundle
    import androidx.activity.ComponentActivity
    import androidx.activity.compose.setContent
    import androidx.activity.enableEdgeToEdge
    import com.arcgismaps.ApiKey
    import com.arcgismaps.ArcGISEnvironment
    import com.arcgismaps.httpcore.authentication.OAuthUserConfiguration
    import com.arcgismaps.toolkit.authentication.AuthenticatorState
    import com.arcgismaps.toolkit.authentication.DialogAuthenticator
    import com.example.app.screens.MainScreen
    import com.example.app.ui.theme.TutorialTheme
    class MainActivity : ComponentActivity() {
    private enum class AuthenticationMode { API_KEY, USER_AUTH }
    private val authenticationMode = AuthenticationMode.API_KEY
    42 collapsed lines
    private val authenticatorState = AuthenticatorState()
    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    when (authenticationMode) {
    AuthenticationMode.API_KEY -> {
    ArcGISEnvironment.apiKey = ApiKey.create("YOUR_ACCESS_TOKEN")
    }
    AuthenticationMode.USER_AUTH -> {
    authenticatorState.oAuthUserConfigurations = listOf(
    OAuthUserConfiguration(
    portalUrl = "https://www.arcgis.com",
    clientId = "YOUR_CLIENT_ID",
    redirectUrl = "YOUR_REDIRECT_URL"
    )
    )
    }
    }
    enableEdgeToEdge()
    setContent {
    TutorialTheme {
    MainScreen()
    if (authenticationMode == AuthenticationMode.USER_AUTH) {
    DialogAuthenticator(authenticatorState)
    }
    }
    }
    }
    }
  2. Set the apiKey property with your API key access token.

    MainActivity.kt
    22 collapsed lines
    package com.example.app
    import android.os.Bundle
    import androidx.activity.ComponentActivity
    import androidx.activity.compose.setContent
    import androidx.activity.enableEdgeToEdge
    import com.arcgismaps.ApiKey
    import com.arcgismaps.ArcGISEnvironment
    import com.arcgismaps.httpcore.authentication.OAuthUserConfiguration
    import com.arcgismaps.toolkit.authentication.AuthenticatorState
    import com.arcgismaps.toolkit.authentication.DialogAuthenticator
    import com.example.app.screens.MainScreen
    import com.example.app.ui.theme.TutorialTheme
    class MainActivity : ComponentActivity() {
    private enum class AuthenticationMode { API_KEY, USER_AUTH }
    private val authenticationMode = AuthenticationMode.API_KEY
    private val authenticatorState = AuthenticatorState()
    override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    when (authenticationMode) {
    AuthenticationMode.API_KEY -> {
    ArcGISEnvironment.apiKey = ApiKey.create("YOUR_ACCESS_TOKEN")
    }
    30 collapsed lines
    AuthenticationMode.USER_AUTH -> {
    authenticatorState.oAuthUserConfigurations = listOf(
    OAuthUserConfiguration(
    portalUrl = "https://www.arcgis.com",
    clientId = "YOUR_CLIENT_ID",
    redirectUrl = "YOUR_REDIRECT_URL"
    )
    )
    }
    }
    enableEdgeToEdge()
    setContent {
    TutorialTheme {
    MainScreen()
    if (authenticationMode == AuthenticationMode.USER_AUTH) {
    DialogAuthenticator(authenticatorState)
    }
    }
    }
    }
    }

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: