Learn how to find an address or place with a search bar and the geocoding service

Geocoding is the process of converting address or place
In this tutorial, you use a search bar in the user interface to access the Geocoding service and search for addresses and places.
Prerequisites
Before starting this tutorial, you need the following:
-
An ArcGIS Location Platform or ArcGIS Online account.
-
A development and deployment environment that meets the system requirements.
-
An IDE for Android development in Kotlin.
Set up authentication
To access the secure ArcGIS location services
You can implement API key authentication or user authentication in this tutorial. Compare the differences below:
API key authentication
- Users are not required to sign in.
- Requires creating an API key credential
API key credentials are an item that contains the parameters used to create and manage long-lived access tokens for API key authentication. They are a type of developer credential. with the correct privileges. - API keys
An API key is a long-lived access token created using API key credentials. They are valid for up to one year and are typically embedded directly into client applications. are long-lived access tokens. - Service usage is billed to the API key owner/developer.
- Simplest authentication method to implement.
- Recommended approach for new ArcGIS developers.
Learn more in API key authentication.
User authentication
- Users are required to sign in with an ArcGIS account
An ArcGIS account is an identity with a user type and set of privileges that can access specific ArcGIS products, tools, APIs, services, and resources. The main account types that can be used for development are an ArcGIS Location Platform account, ArcGIS Online account, and ArcGIS Enterprise account. ArcGIS Location Platform and ArcGIS Online accounts are also associated with a subscription. . - User accounts must have privilege
Privileges are a set of permissions assigned to ArcGIS accounts, developer credentials, and applications that grant access to secure resources and functionality in ArcGIS. to access the ArcGIS servicesA service, also known as an ArcGIS service, is software that supports an ArcGIS REST API and provides geospatial functionality or data. A service can be hosted by Esri or in ArcGIS Enterprise. used in application. - Requires creating OAuth credentials
OAuth credentials are an item that contains parameters required to implement user authentication or app authentication, including a .client_id,client_secret, and redirect URIs. They are a type of developer credential. - Application uses a redirect URL and client ID.
- Service usage is billed to the organization of the user signed into the application.
Learn more in User authentication.
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
-
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. :- Privileges
- Location services > Basemaps
- Location services > Geocoding
- Privileges
-
Copy and paste the API key access token into a safe location. It will be used in a later step.
Create new OAuth credentials to access the secure resources used in this tutorial.
-
Complete the Create OAuth credentials for user authentication tutorial to obtain a Client ID and Redirect URL.
A
Client IDuniquely identifies your app on the authenticating server. If the server cannot find an app with the provided Client ID, it will not proceed with authentication.The
Redirect URL(also referred to as a callback url) is used to identify a response from the authenticating server when the system returns control back to your app after an OAuth login. Since it does not necessarily represent a valid endpoint that a user could navigate to, the redirect URL can use a custom scheme, such asmy-app://auth. It is important to make sure the redirect URL used in your app’s code matches a redirect URL configured on the authenticating server. -
Copy and paste the Client ID and Redirect URL into a safe location. They will be used in a later step.
All users that access this application need account privileges
Develop or download
You have two options for completing this tutorial:
Option 1: Develop the code
Open an Android Studio project
-
Open the project you created by completing the Display a map tutorial.
-
Continue with the following instructions to add a search bar to the user interface and search for an address or place using the ArcGIS Geocoding service
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. . -
Modify the old project for use in this new tutorial.
-
On your file system, delete the .idea folder, if present, at the top level of your project.
-
In the Android view, open app > res > values > strings.xml.
In the
<string name="app_name">element, change the text content to Search for an address.strings.xml<resources><string name="app_name">Search for an address</string></resources> -
In the Android view, open Gradle Scripts > settings.gradle.kts.
Change the value of
rootProject.nameto “Search for an address”.settings.gradle.kts14 collapsed linespluginManagement {repositories {google {content {includeGroupByRegex("com\\.android.*")includeGroupByRegex("com\\.google.*")includeGroupByRegex("androidx.*")}}mavenCentral()gradlePluginPortal()}}dependencyResolutionManagement {repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)repositories {google()mavenCentral()maven { url = uri("https://esri.jfrog.io/artifactory/arcgis") }}}rootProject.name = "Search for an address"include(":app") -
Click File > Sync Project with Gradle files. Android Studio will recognize your changes and create a new .idea folder.
-
Set developer credentials
If you implemented API key authenticationLocatorTask. To create an API Key access token that has the Basemaps and Geocoding privileges, see the Set up authentication step and then follow the instructions below.
-
In the Android view of Android Studio, open app > kotlin+java > com.example.app > MainActivity.
-
In the
onCreate()lifecycle method of theMainActivityclass, set theArcGISEnvironment.apiKeyproperty by callingApiKey.create(). Pass in your API key access token as a string and don’t forget the double quotes. Do this before thesetContentblock.MainActivity.ktoverride 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.
Use the Authenticator toolkit component to manage your OAuth credentialsclient_id, client_secret, and redirect URIs. They are a type of developer credential. ArcGISEnvironment.
-
Open the app > kotlin+java > com.example.app > MainActivity.kt file.
-
Set your
clientIDandredirectURLvalues.MainActivity.ktauthenticatorState.oAuthUserConfigurations = listOf(OAuthUserConfiguration(portalUrl = "https://www.arcgis.com",clientId = "YOUR_CLIENT_ID",redirectUrl = "YOUR_REDIRECT_URL"))
Best Practice: The OAuth credentials are stored directly in the code as a convenience for this tutorial. Do not store credentials directly in source code in a production environment.
Add import statements and some Compose variables
-
Modify import statements to reference the packages and classes required for this tutorial.
Theme.kt@file:OptIn(ExperimentalMaterial3Api::class)package com.example.app.screensimport android.content.Contextimport android.widget.Toastimport androidx.compose.foundation.layout.Columnimport androidx.compose.foundation.layout.fillMaxSizeimport androidx.compose.foundation.layout.fillMaxWidthimport androidx.compose.foundation.layout.paddingimport androidx.compose.material.icons.Iconsimport androidx.compose.material.icons.filled.Searchimport androidx.compose.material3.ExperimentalMaterial3Apiimport androidx.compose.material3.Iconimport androidx.compose.material3.Scaffoldimport androidx.compose.material3.SearchBarimport androidx.compose.material3.SearchBarDefaultsimport androidx.compose.material3.Textimport androidx.compose.material3.TopAppBarimport androidx.compose.runtime.Composableimport androidx.compose.runtime.getValueimport androidx.compose.runtime.mutableStateOfimport androidx.compose.runtime.rememberimport androidx.compose.runtime.rememberCoroutineScopeimport androidx.compose.runtime.setValueimport androidx.compose.ui.Modifierimport androidx.compose.ui.platform.LocalContextimport androidx.compose.ui.platform.LocalFocusManagerimport androidx.compose.ui.res.stringResourceimport com.arcgismaps.Colorimport com.arcgismaps.geometry.SpatialReferenceimport com.arcgismaps.mapping.ArcGISMapimport com.arcgismaps.mapping.BasemapStyleimport com.arcgismaps.mapping.Viewpointimport com.arcgismaps.mapping.symbology.HorizontalAlignmentimport com.arcgismaps.mapping.symbology.SimpleMarkerSymbolimport com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyleimport com.arcgismaps.mapping.symbology.TextSymbolimport com.arcgismaps.mapping.symbology.VerticalAlignmentimport com.arcgismaps.mapping.view.Graphicimport com.arcgismaps.mapping.view.GraphicsOverlayimport com.arcgismaps.tasks.geocode.GeocodeParametersimport com.arcgismaps.tasks.geocode.GeocodeResultimport com.arcgismaps.tasks.geocode.LocatorTaskimport com.arcgismaps.toolkit.geoviewcompose.MapViewimport com.arcgismaps.toolkit.geoviewcompose.MapViewProxyimport com.example.app.Rimport kotlinx.coroutines.CoroutineScopeimport kotlinx.coroutines.Job -
In the
MainScreencomposable, create Compose variables that will be passed to various functions in theMainScreen.ktfile.These are remembered state variables and use either
remember()orrememberCoroutineScope()Briefly, these variables are as follows:
-
context: The local context of your app. -
coroutineScope: Set torememberCoroutineScope(). You will use this variable to launch a coroutine. -
focusManager: AFocusManagerwill allow you to clear focus from the search bar, dismissing the device keyboard after a search is submitted. -
queryText: The address that the user enters in the search bar. -
currentJob: ThecurrentJobvariable is of typeMutableState<Job?>, which references the Kotlin coroutineJob. (It does not reference the interfaceJobfrom ArcGIS Maps SDK for Kotlin.) Note thatcoroutineScope.launch {}returns a Kotlin coroutineJob. -
graphicsOverlay: AGraphicsOverlayto hold the text symbol (the search address) and a red square symbol (the found location on the map). -
graphicsOverlay: A list ofGraphicsOverlay. You pass this list to theMapView. -
mapViewProxy: AMapViewProxy, defined in ArcGIS Maps SDK for Kotlin Toolkit, allows you to set the view point on the map view. -
currentSpatialReference: The currentSpatialReference.
Theme.kt@Composablefun MainScreen() {val context = LocalContext.currentval coroutineScope = rememberCoroutineScope()// The focus manager is used to dismiss keyboard after search query submission.val focusManager = LocalFocusManager.currentvar 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
-
Define a top-level
suspendfunction namedsearchAddress(). Declare the parameters shown below.MainScreen.kt93 collapsed lines@file:OptIn(ExperimentalMaterial3Api::class)package com.example.app.screensimport android.content.Contextimport android.widget.Toastimport androidx.compose.foundation.layout.Columnimport androidx.compose.foundation.layout.fillMaxSizeimport androidx.compose.foundation.layout.fillMaxWidthimport androidx.compose.foundation.layout.paddingimport androidx.compose.material.icons.Iconsimport androidx.compose.material.icons.filled.Searchimport androidx.compose.material3.ExperimentalMaterial3Apiimport androidx.compose.material3.Iconimport androidx.compose.material3.Scaffoldimport androidx.compose.material3.SearchBarimport androidx.compose.material3.SearchBarDefaultsimport androidx.compose.material3.Textimport androidx.compose.material3.TopAppBarimport androidx.compose.runtime.Composableimport androidx.compose.runtime.getValueimport androidx.compose.runtime.mutableStateOfimport androidx.compose.runtime.rememberimport androidx.compose.runtime.rememberCoroutineScopeimport androidx.compose.runtime.setValueimport androidx.compose.ui.Modifierimport androidx.compose.ui.platform.LocalContextimport androidx.compose.ui.platform.LocalFocusManagerimport androidx.compose.ui.res.stringResourceimport com.arcgismaps.Colorimport com.arcgismaps.geometry.SpatialReferenceimport com.arcgismaps.mapping.ArcGISMapimport com.arcgismaps.mapping.BasemapStyleimport com.arcgismaps.mapping.Viewpointimport com.arcgismaps.mapping.symbology.HorizontalAlignmentimport com.arcgismaps.mapping.symbology.SimpleMarkerSymbolimport com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyleimport com.arcgismaps.mapping.symbology.TextSymbolimport com.arcgismaps.mapping.symbology.VerticalAlignmentimport com.arcgismaps.mapping.view.Graphicimport com.arcgismaps.mapping.view.GraphicsOverlayimport com.arcgismaps.tasks.geocode.GeocodeParametersimport com.arcgismaps.tasks.geocode.GeocodeResultimport com.arcgismaps.tasks.geocode.LocatorTaskimport com.arcgismaps.toolkit.geoviewcompose.MapViewimport com.arcgismaps.toolkit.geoviewcompose.MapViewProxyimport com.example.app.Rimport kotlinx.coroutines.CoroutineScopeimport kotlinx.coroutines.Jobimport kotlinx.coroutines.launch@Composablefun MainScreen() {val context = LocalContext.currentval coroutineScope = rememberCoroutineScope()// The focus manager is used to dismiss keyboard after search query submission.val focusManager = LocalFocusManager.currentvar 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 linesfun showMessage(context: Context, message: String) {Toast.makeText(context, message, Toast.LENGTH_LONG).show()} -
Create a
LocatorTaskbased on the Geocoding service.A locator task is used to find the location of an address (geocode) or to interpolate an address for a location (reverse geocode). An address includes any type of information that distinguishes a place. A locator
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. involves finding matching locations for a given address. Reverse-geocoding is the opposite and finds the closest address for a given location.MainScreen.kt93 collapsed lines@file:OptIn(ExperimentalMaterial3Api::class)package com.example.app.screensimport android.content.Contextimport android.widget.Toastimport androidx.compose.foundation.layout.Columnimport androidx.compose.foundation.layout.fillMaxSizeimport androidx.compose.foundation.layout.fillMaxWidthimport androidx.compose.foundation.layout.paddingimport androidx.compose.material.icons.Iconsimport androidx.compose.material.icons.filled.Searchimport androidx.compose.material3.ExperimentalMaterial3Apiimport androidx.compose.material3.Iconimport androidx.compose.material3.Scaffoldimport androidx.compose.material3.SearchBarimport androidx.compose.material3.SearchBarDefaultsimport androidx.compose.material3.Textimport androidx.compose.material3.TopAppBarimport androidx.compose.runtime.Composableimport androidx.compose.runtime.getValueimport androidx.compose.runtime.mutableStateOfimport androidx.compose.runtime.rememberimport androidx.compose.runtime.rememberCoroutineScopeimport androidx.compose.runtime.setValueimport androidx.compose.ui.Modifierimport androidx.compose.ui.platform.LocalContextimport androidx.compose.ui.platform.LocalFocusManagerimport androidx.compose.ui.res.stringResourceimport com.arcgismaps.Colorimport com.arcgismaps.geometry.SpatialReferenceimport com.arcgismaps.mapping.ArcGISMapimport com.arcgismaps.mapping.BasemapStyleimport com.arcgismaps.mapping.Viewpointimport com.arcgismaps.mapping.symbology.HorizontalAlignmentimport com.arcgismaps.mapping.symbology.SimpleMarkerSymbolimport com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyleimport com.arcgismaps.mapping.symbology.TextSymbolimport com.arcgismaps.mapping.symbology.VerticalAlignmentimport com.arcgismaps.mapping.view.Graphicimport com.arcgismaps.mapping.view.GraphicsOverlayimport com.arcgismaps.tasks.geocode.GeocodeParametersimport com.arcgismaps.tasks.geocode.GeocodeResultimport com.arcgismaps.tasks.geocode.LocatorTaskimport com.arcgismaps.toolkit.geoviewcompose.MapViewimport com.arcgismaps.toolkit.geoviewcompose.MapViewProxyimport com.example.app.Rimport kotlinx.coroutines.CoroutineScopeimport kotlinx.coroutines.Jobimport kotlinx.coroutines.launch@Composablefun MainScreen() {val context = LocalContext.currentval coroutineScope = rememberCoroutineScope()// The focus manager is used to dismiss keyboard after search query submission.val focusManager = LocalFocusManager.currentvar 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 linesfun showMessage(context: Context, message: String) {Toast.makeText(context, message, Toast.LENGTH_LONG).show()} -
Create a new
GeocodeParametersand define some of its properties.- Add the names of attributes to return to the
GeocodeParameters.resultAttributeNamescollection. An asterisk (*) indicates all attributes. - Set the maximum number of results to be returned with
GeocodeParameters.maxResults. Results are ordered byscore, so that the first result has the best match score (ranging from 0 for no match to 100 for the best match). - Set the spatial reference
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. for result locations withGeocodeParameters.outputSpatialReference. By default, the output spatial reference is defined by the geocode service. For optimal performance when displaying the geocode result, you can ensure that returned coordinates match those of the map view by providing the map view’s spatial reference.
When geocoding an address, you can optionally provide
GeocodeParametersto control certain aspects of the geocoding operation and specify the kinds of results to return from the locator task. Learn more about these parameters in theGeocodeParameters. For a list of attributes returned with geocode results, see Geocoding service output in the ArcGIS services reference.MainScreen.kt93 collapsed lines@file:OptIn(ExperimentalMaterial3Api::class)package com.example.app.screensimport android.content.Contextimport android.widget.Toastimport androidx.compose.foundation.layout.Columnimport androidx.compose.foundation.layout.fillMaxSizeimport androidx.compose.foundation.layout.fillMaxWidthimport androidx.compose.foundation.layout.paddingimport androidx.compose.material.icons.Iconsimport androidx.compose.material.icons.filled.Searchimport androidx.compose.material3.ExperimentalMaterial3Apiimport androidx.compose.material3.Iconimport androidx.compose.material3.Scaffoldimport androidx.compose.material3.SearchBarimport androidx.compose.material3.SearchBarDefaultsimport androidx.compose.material3.Textimport androidx.compose.material3.TopAppBarimport androidx.compose.runtime.Composableimport androidx.compose.runtime.getValueimport androidx.compose.runtime.mutableStateOfimport androidx.compose.runtime.rememberimport androidx.compose.runtime.rememberCoroutineScopeimport androidx.compose.runtime.setValueimport androidx.compose.ui.Modifierimport androidx.compose.ui.platform.LocalContextimport androidx.compose.ui.platform.LocalFocusManagerimport androidx.compose.ui.res.stringResourceimport com.arcgismaps.Colorimport com.arcgismaps.geometry.SpatialReferenceimport com.arcgismaps.mapping.ArcGISMapimport com.arcgismaps.mapping.BasemapStyleimport com.arcgismaps.mapping.Viewpointimport com.arcgismaps.mapping.symbology.HorizontalAlignmentimport com.arcgismaps.mapping.symbology.SimpleMarkerSymbolimport com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyleimport com.arcgismaps.mapping.symbology.TextSymbolimport com.arcgismaps.mapping.symbology.VerticalAlignmentimport com.arcgismaps.mapping.view.Graphicimport com.arcgismaps.mapping.view.GraphicsOverlayimport com.arcgismaps.tasks.geocode.GeocodeParametersimport com.arcgismaps.tasks.geocode.GeocodeResultimport com.arcgismaps.tasks.geocode.LocatorTaskimport com.arcgismaps.toolkit.geoviewcompose.MapViewimport com.arcgismaps.toolkit.geoviewcompose.MapViewProxyimport com.example.app.Rimport kotlinx.coroutines.CoroutineScopeimport kotlinx.coroutines.Jobimport kotlinx.coroutines.launch@Composablefun MainScreen() {val context = LocalContext.currentval coroutineScope = rememberCoroutineScope()// The focus manager is used to dismiss keyboard after search query submission.val focusManager = LocalFocusManager.currentvar 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 parametersval geocodeParameters = GeocodeParameters().apply {resultAttributeNames.add("*")maxResults = 1outputSpatialReference = currentSpatialReference}}4 collapsed linesfun showMessage(context: Context, message: String) {Toast.makeText(context, message, Toast.LENGTH_LONG).show()} - Add the names of attributes to return to the
-
To find the location for the address that the user entered, call
LocatorTask.geocode(), passing thequeryandgeocodeParametersvariables.In the
.onSuccessblock, call thehandleGeocodeResults()function (which we will define in a later section). Pass parameters tohandleGeocodeResults()as shown below.The geocode results are implemented as a list of
GeocodeResultobjects. In this tutorial, either one or zero results will be returned, as the maximum results parameter was set to 1.MainScreen.kt93 collapsed lines@file:OptIn(ExperimentalMaterial3Api::class)package com.example.app.screensimport android.content.Contextimport android.widget.Toastimport androidx.compose.foundation.layout.Columnimport androidx.compose.foundation.layout.fillMaxSizeimport androidx.compose.foundation.layout.fillMaxWidthimport androidx.compose.foundation.layout.paddingimport androidx.compose.material.icons.Iconsimport androidx.compose.material.icons.filled.Searchimport androidx.compose.material3.ExperimentalMaterial3Apiimport androidx.compose.material3.Iconimport androidx.compose.material3.Scaffoldimport androidx.compose.material3.SearchBarimport androidx.compose.material3.SearchBarDefaultsimport androidx.compose.material3.Textimport androidx.compose.material3.TopAppBarimport androidx.compose.runtime.Composableimport androidx.compose.runtime.getValueimport androidx.compose.runtime.mutableStateOfimport androidx.compose.runtime.rememberimport androidx.compose.runtime.rememberCoroutineScopeimport androidx.compose.runtime.setValueimport androidx.compose.ui.Modifierimport androidx.compose.ui.platform.LocalContextimport androidx.compose.ui.platform.LocalFocusManagerimport androidx.compose.ui.res.stringResourceimport com.arcgismaps.Colorimport com.arcgismaps.geometry.SpatialReferenceimport com.arcgismaps.mapping.ArcGISMapimport com.arcgismaps.mapping.BasemapStyleimport com.arcgismaps.mapping.Viewpointimport com.arcgismaps.mapping.symbology.HorizontalAlignmentimport com.arcgismaps.mapping.symbology.SimpleMarkerSymbolimport com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyleimport com.arcgismaps.mapping.symbology.TextSymbolimport com.arcgismaps.mapping.symbology.VerticalAlignmentimport com.arcgismaps.mapping.view.Graphicimport com.arcgismaps.mapping.view.GraphicsOverlayimport com.arcgismaps.tasks.geocode.GeocodeParametersimport com.arcgismaps.tasks.geocode.GeocodeResultimport com.arcgismaps.tasks.geocode.LocatorTaskimport com.arcgismaps.toolkit.geoviewcompose.MapViewimport com.arcgismaps.toolkit.geoviewcompose.MapViewProxyimport com.example.app.Rimport kotlinx.coroutines.CoroutineScopeimport kotlinx.coroutines.Jobimport kotlinx.coroutines.launch@Composablefun MainScreen() {val context = LocalContext.currentval coroutineScope = rememberCoroutineScope()// The focus manager is used to dismiss keyboard after search query submission.val focusManager = LocalFocusManager.currentvar 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 parametersval geocodeParameters = GeocodeParameters().apply {resultAttributeNames.add("*")maxResults = 1outputSpatialReference = currentSpatialReference}// Search for the addresslocatorTask.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 linesfun 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.
-
Define a top-level function named
createTextGraphic()and declare aGeocodeResultparameter. For thetextof theTextSymbol, use theGeocodeResult.labelproperty. For thegeometryof theGraphic, use theGeocodeResult.displayLocationproperty.MainScreen.kt124 collapsed lines@file:OptIn(ExperimentalMaterial3Api::class)package com.example.app.screensimport android.content.Contextimport android.widget.Toastimport androidx.compose.foundation.layout.Columnimport androidx.compose.foundation.layout.fillMaxSizeimport androidx.compose.foundation.layout.fillMaxWidthimport androidx.compose.foundation.layout.paddingimport androidx.compose.material.icons.Iconsimport androidx.compose.material.icons.filled.Searchimport androidx.compose.material3.ExperimentalMaterial3Apiimport androidx.compose.material3.Iconimport androidx.compose.material3.Scaffoldimport androidx.compose.material3.SearchBarimport androidx.compose.material3.SearchBarDefaultsimport androidx.compose.material3.Textimport androidx.compose.material3.TopAppBarimport androidx.compose.runtime.Composableimport androidx.compose.runtime.getValueimport androidx.compose.runtime.mutableStateOfimport androidx.compose.runtime.rememberimport androidx.compose.runtime.rememberCoroutineScopeimport androidx.compose.runtime.setValueimport androidx.compose.ui.Modifierimport androidx.compose.ui.platform.LocalContextimport androidx.compose.ui.platform.LocalFocusManagerimport androidx.compose.ui.res.stringResourceimport com.arcgismaps.Colorimport com.arcgismaps.geometry.SpatialReferenceimport com.arcgismaps.mapping.ArcGISMapimport com.arcgismaps.mapping.BasemapStyleimport com.arcgismaps.mapping.Viewpointimport com.arcgismaps.mapping.symbology.HorizontalAlignmentimport com.arcgismaps.mapping.symbology.SimpleMarkerSymbolimport com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyleimport com.arcgismaps.mapping.symbology.TextSymbolimport com.arcgismaps.mapping.symbology.VerticalAlignmentimport com.arcgismaps.mapping.view.Graphicimport com.arcgismaps.mapping.view.GraphicsOverlayimport com.arcgismaps.tasks.geocode.GeocodeParametersimport com.arcgismaps.tasks.geocode.GeocodeResultimport com.arcgismaps.tasks.geocode.LocatorTaskimport com.arcgismaps.toolkit.geoviewcompose.MapViewimport com.arcgismaps.toolkit.geoviewcompose.MapViewProxyimport com.example.app.Rimport kotlinx.coroutines.CoroutineScopeimport kotlinx.coroutines.Jobimport kotlinx.coroutines.launch@Composablefun MainScreen() {val context = LocalContext.currentval coroutineScope = rememberCoroutineScope()// The focus manager is used to dismiss keyboard after search query submission.val focusManager = LocalFocusManager.currentvar 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 parametersval geocodeParameters = GeocodeParameters().apply {resultAttributeNames.add("*")maxResults = 1outputSpatialReference = currentSpatialReference}// Search for the addresslocatorTask.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 = 8fhaloColor = Color.whitehaloWidth = 2f}return Graphic(geometry = geocodeResult.displayLocation,symbol = textSymbol)}4 collapsed linesfun showMessage(context: Context, message: String) {Toast.makeText(context, message, Toast.LENGTH_LONG).show()} -
Define a top-level function named
createMarkerGraphic()and declare aGeocodeResultparameter. For thegeometryof theGraphic, use theGeocodeResult.labelproperty. For theattributesof theGraphic, use theGeocodeResult.attributesproperty.MainScreen.kt142 collapsed lines@file:OptIn(ExperimentalMaterial3Api::class)package com.example.app.screensimport android.content.Contextimport android.widget.Toastimport androidx.compose.foundation.layout.Columnimport androidx.compose.foundation.layout.fillMaxSizeimport androidx.compose.foundation.layout.fillMaxWidthimport androidx.compose.foundation.layout.paddingimport androidx.compose.material.icons.Iconsimport androidx.compose.material.icons.filled.Searchimport androidx.compose.material3.ExperimentalMaterial3Apiimport androidx.compose.material3.Iconimport androidx.compose.material3.Scaffoldimport androidx.compose.material3.SearchBarimport androidx.compose.material3.SearchBarDefaultsimport androidx.compose.material3.Textimport androidx.compose.material3.TopAppBarimport androidx.compose.runtime.Composableimport androidx.compose.runtime.getValueimport androidx.compose.runtime.mutableStateOfimport androidx.compose.runtime.rememberimport androidx.compose.runtime.rememberCoroutineScopeimport androidx.compose.runtime.setValueimport androidx.compose.ui.Modifierimport androidx.compose.ui.platform.LocalContextimport androidx.compose.ui.platform.LocalFocusManagerimport androidx.compose.ui.res.stringResourceimport com.arcgismaps.Colorimport com.arcgismaps.geometry.SpatialReferenceimport com.arcgismaps.mapping.ArcGISMapimport com.arcgismaps.mapping.BasemapStyleimport com.arcgismaps.mapping.Viewpointimport com.arcgismaps.mapping.symbology.HorizontalAlignmentimport com.arcgismaps.mapping.symbology.SimpleMarkerSymbolimport com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyleimport com.arcgismaps.mapping.symbology.TextSymbolimport com.arcgismaps.mapping.symbology.VerticalAlignmentimport com.arcgismaps.mapping.view.Graphicimport com.arcgismaps.mapping.view.GraphicsOverlayimport com.arcgismaps.tasks.geocode.GeocodeParametersimport com.arcgismaps.tasks.geocode.GeocodeResultimport com.arcgismaps.tasks.geocode.LocatorTaskimport com.arcgismaps.toolkit.geoviewcompose.MapViewimport com.arcgismaps.toolkit.geoviewcompose.MapViewProxyimport com.example.app.Rimport kotlinx.coroutines.CoroutineScopeimport kotlinx.coroutines.Jobimport kotlinx.coroutines.launch@Composablefun MainScreen() {val context = LocalContext.currentval coroutineScope = rememberCoroutineScope()// The focus manager is used to dismiss keyboard after search query submission.val focusManager = LocalFocusManager.currentvar 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 parametersval geocodeParameters = GeocodeParameters().apply {resultAttributeNames.add("*")maxResults = 1outputSpatialReference = currentSpatialReference}// Search for the addresslocatorTask.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 = 8fhaloColor = Color.whitehaloWidth = 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 linesfun 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
-
Define a
suspendfunction namedhandleGeocodeResults(). Declare the parameters shown below.MainScreen.kt124 collapsed lines@file:OptIn(ExperimentalMaterial3Api::class)package com.example.app.screensimport android.content.Contextimport android.widget.Toastimport androidx.compose.foundation.layout.Columnimport androidx.compose.foundation.layout.fillMaxSizeimport androidx.compose.foundation.layout.fillMaxWidthimport androidx.compose.foundation.layout.paddingimport androidx.compose.material.icons.Iconsimport androidx.compose.material.icons.filled.Searchimport androidx.compose.material3.ExperimentalMaterial3Apiimport androidx.compose.material3.Iconimport androidx.compose.material3.Scaffoldimport androidx.compose.material3.SearchBarimport androidx.compose.material3.SearchBarDefaultsimport androidx.compose.material3.Textimport androidx.compose.material3.TopAppBarimport androidx.compose.runtime.Composableimport androidx.compose.runtime.getValueimport androidx.compose.runtime.mutableStateOfimport androidx.compose.runtime.rememberimport androidx.compose.runtime.rememberCoroutineScopeimport androidx.compose.runtime.setValueimport androidx.compose.ui.Modifierimport androidx.compose.ui.platform.LocalContextimport androidx.compose.ui.platform.LocalFocusManagerimport androidx.compose.ui.res.stringResourceimport com.arcgismaps.Colorimport com.arcgismaps.geometry.SpatialReferenceimport com.arcgismaps.mapping.ArcGISMapimport com.arcgismaps.mapping.BasemapStyleimport com.arcgismaps.mapping.Viewpointimport com.arcgismaps.mapping.symbology.HorizontalAlignmentimport com.arcgismaps.mapping.symbology.SimpleMarkerSymbolimport com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyleimport com.arcgismaps.mapping.symbology.TextSymbolimport com.arcgismaps.mapping.symbology.VerticalAlignmentimport com.arcgismaps.mapping.view.Graphicimport com.arcgismaps.mapping.view.GraphicsOverlayimport com.arcgismaps.tasks.geocode.GeocodeParametersimport com.arcgismaps.tasks.geocode.GeocodeResultimport com.arcgismaps.tasks.geocode.LocatorTaskimport com.arcgismaps.toolkit.geoviewcompose.MapViewimport com.arcgismaps.toolkit.geoviewcompose.MapViewProxyimport com.example.app.Rimport kotlinx.coroutines.CoroutineScopeimport kotlinx.coroutines.Jobimport kotlinx.coroutines.launch@Composablefun MainScreen() {val context = LocalContext.currentval coroutineScope = rememberCoroutineScope()// The focus manager is used to dismiss keyboard after search query submission.val focusManager = LocalFocusManager.currentvar 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 parametersval geocodeParameters = GeocodeParameters().apply {resultAttributeNames.add("*")maxResults = 1outputSpatialReference = currentSpatialReference}// Search for the addresslocatorTask.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 linesfun createTextGraphic(geocodeResult: GeocodeResult): Graphic {val textSymbol = TextSymbol(text = geocodeResult.label,color = Color.black,size = 18f,horizontalAlignment = HorizontalAlignment.Center,verticalAlignment = VerticalAlignment.Bottom).apply {offsetY = 8fhaloColor = Color.whitehaloWidth = 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()} -
If the
geocodeResultslist is not empty, do the following: First, get the firstGeocodeResultfrom thegeocodeResultslist. Next, callcreateTextGraphic()andcreateMarkerGraphic(), passing that geocode result to each. Last, add the two graphics to theGraphicsOverlay.MainScreen.kt124 collapsed lines@file:OptIn(ExperimentalMaterial3Api::class)package com.example.app.screensimport android.content.Contextimport android.widget.Toastimport androidx.compose.foundation.layout.Columnimport androidx.compose.foundation.layout.fillMaxSizeimport androidx.compose.foundation.layout.fillMaxWidthimport androidx.compose.foundation.layout.paddingimport androidx.compose.material.icons.Iconsimport androidx.compose.material.icons.filled.Searchimport androidx.compose.material3.ExperimentalMaterial3Apiimport androidx.compose.material3.Iconimport androidx.compose.material3.Scaffoldimport androidx.compose.material3.SearchBarimport androidx.compose.material3.SearchBarDefaultsimport androidx.compose.material3.Textimport androidx.compose.material3.TopAppBarimport androidx.compose.runtime.Composableimport androidx.compose.runtime.getValueimport androidx.compose.runtime.mutableStateOfimport androidx.compose.runtime.rememberimport androidx.compose.runtime.rememberCoroutineScopeimport androidx.compose.runtime.setValueimport androidx.compose.ui.Modifierimport androidx.compose.ui.platform.LocalContextimport androidx.compose.ui.platform.LocalFocusManagerimport androidx.compose.ui.res.stringResourceimport com.arcgismaps.Colorimport com.arcgismaps.geometry.SpatialReferenceimport com.arcgismaps.mapping.ArcGISMapimport com.arcgismaps.mapping.BasemapStyleimport com.arcgismaps.mapping.Viewpointimport com.arcgismaps.mapping.symbology.HorizontalAlignmentimport com.arcgismaps.mapping.symbology.SimpleMarkerSymbolimport com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyleimport com.arcgismaps.mapping.symbology.TextSymbolimport com.arcgismaps.mapping.symbology.VerticalAlignmentimport com.arcgismaps.mapping.view.Graphicimport com.arcgismaps.mapping.view.GraphicsOverlayimport com.arcgismaps.tasks.geocode.GeocodeParametersimport com.arcgismaps.tasks.geocode.GeocodeResultimport com.arcgismaps.tasks.geocode.LocatorTaskimport com.arcgismaps.toolkit.geoviewcompose.MapViewimport com.arcgismaps.toolkit.geoviewcompose.MapViewProxyimport com.example.app.Rimport kotlinx.coroutines.CoroutineScopeimport kotlinx.coroutines.Jobimport kotlinx.coroutines.launch@Composablefun MainScreen() {val context = LocalContext.currentval coroutineScope = rememberCoroutineScope()// The focus manager is used to dismiss keyboard after search query submission.val focusManager = LocalFocusManager.currentvar 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 parametersval geocodeParameters = GeocodeParameters().apply {resultAttributeNames.add("*")maxResults = 1outputSpatialReference = currentSpatialReference}// Search for the addresslocatorTask.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 linesfun createTextGraphic(geocodeResult: GeocodeResult): Graphic {val textSymbol = TextSymbol(text = geocodeResult.label,color = Color.black,size = 18f,horizontalAlignment = HorizontalAlignment.Center,verticalAlignment = VerticalAlignment.Bottom).apply {offsetY = 8fhaloColor = Color.whitehaloWidth = 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()} -
Inside a
coroutineScope.launchblock, get the display location (which is aPoint) from the geocode result and assign it to a variable namedcenterPoint. Then callMapViewProxy.setViewpointCenter(), which is defined in the ArcGIS Maps SDK for Kotlin Toolkit, and passcenterPoint.Last, add code to handle the case where the
geocodeResultslist is empty.MainScreen.kt124 collapsed lines@file:OptIn(ExperimentalMaterial3Api::class)package com.example.app.screensimport android.content.Contextimport android.widget.Toastimport androidx.compose.foundation.layout.Columnimport androidx.compose.foundation.layout.fillMaxSizeimport androidx.compose.foundation.layout.fillMaxWidthimport androidx.compose.foundation.layout.paddingimport androidx.compose.material.icons.Iconsimport androidx.compose.material.icons.filled.Searchimport androidx.compose.material3.ExperimentalMaterial3Apiimport androidx.compose.material3.Iconimport androidx.compose.material3.Scaffoldimport androidx.compose.material3.SearchBarimport androidx.compose.material3.SearchBarDefaultsimport androidx.compose.material3.Textimport androidx.compose.material3.TopAppBarimport androidx.compose.runtime.Composableimport androidx.compose.runtime.getValueimport androidx.compose.runtime.mutableStateOfimport androidx.compose.runtime.rememberimport androidx.compose.runtime.rememberCoroutineScopeimport androidx.compose.runtime.setValueimport androidx.compose.ui.Modifierimport androidx.compose.ui.platform.LocalContextimport androidx.compose.ui.platform.LocalFocusManagerimport androidx.compose.ui.res.stringResourceimport com.arcgismaps.Colorimport com.arcgismaps.geometry.SpatialReferenceimport com.arcgismaps.mapping.ArcGISMapimport com.arcgismaps.mapping.BasemapStyleimport com.arcgismaps.mapping.Viewpointimport com.arcgismaps.mapping.symbology.HorizontalAlignmentimport com.arcgismaps.mapping.symbology.SimpleMarkerSymbolimport com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyleimport com.arcgismaps.mapping.symbology.TextSymbolimport com.arcgismaps.mapping.symbology.VerticalAlignmentimport com.arcgismaps.mapping.view.Graphicimport com.arcgismaps.mapping.view.GraphicsOverlayimport com.arcgismaps.tasks.geocode.GeocodeParametersimport com.arcgismaps.tasks.geocode.GeocodeResultimport com.arcgismaps.tasks.geocode.LocatorTaskimport com.arcgismaps.toolkit.geoviewcompose.MapViewimport com.arcgismaps.toolkit.geoviewcompose.MapViewProxyimport com.example.app.Rimport kotlinx.coroutines.CoroutineScopeimport kotlinx.coroutines.Jobimport kotlinx.coroutines.launch@Composablefun MainScreen() {val context = LocalContext.currentval coroutineScope = rememberCoroutineScope()// The focus manager is used to dismiss keyboard after search query submission.val focusManager = LocalFocusManager.currentvar 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 parametersval geocodeParameters = GeocodeParameters().apply {resultAttributeNames.add("*")maxResults = 1outputSpatialReference = currentSpatialReference}// Search for the addresslocatorTask.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 linesfun createTextGraphic(geocodeResult: GeocodeResult): Graphic {val textSymbol = TextSymbol(text = geocodeResult.label,color = Color.black,size = 18f,horizontalAlignment = HorizontalAlignment.Center,verticalAlignment = VerticalAlignment.Bottom).apply {offsetY = 8fhaloColor = Color.whitehaloWidth = 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.
-
Inside the
Scaffoldblock, find theMapViewfrom the Display a map tutorial and replace it with a call ofColumn. AColumnallows you to display the search bar at the top of the screen and the map view directly below.MainScreen.kt51 collapsed lines@file:OptIn(ExperimentalMaterial3Api::class)package com.example.app.screensimport android.content.Contextimport android.widget.Toastimport androidx.compose.foundation.layout.Columnimport androidx.compose.foundation.layout.fillMaxSizeimport androidx.compose.foundation.layout.fillMaxWidthimport androidx.compose.foundation.layout.paddingimport androidx.compose.material.icons.Iconsimport androidx.compose.material.icons.filled.Searchimport androidx.compose.material3.ExperimentalMaterial3Apiimport androidx.compose.material3.Iconimport androidx.compose.material3.Scaffoldimport androidx.compose.material3.SearchBarimport androidx.compose.material3.SearchBarDefaultsimport androidx.compose.material3.Textimport androidx.compose.material3.TopAppBarimport androidx.compose.runtime.Composableimport androidx.compose.runtime.getValueimport androidx.compose.runtime.mutableStateOfimport androidx.compose.runtime.rememberimport androidx.compose.runtime.rememberCoroutineScopeimport androidx.compose.runtime.setValueimport androidx.compose.ui.Modifierimport androidx.compose.ui.platform.LocalContextimport androidx.compose.ui.platform.LocalFocusManagerimport androidx.compose.ui.res.stringResourceimport com.arcgismaps.Colorimport com.arcgismaps.geometry.SpatialReferenceimport com.arcgismaps.mapping.ArcGISMapimport com.arcgismaps.mapping.BasemapStyleimport com.arcgismaps.mapping.Viewpointimport com.arcgismaps.mapping.symbology.HorizontalAlignmentimport com.arcgismaps.mapping.symbology.SimpleMarkerSymbolimport com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyleimport com.arcgismaps.mapping.symbology.TextSymbolimport com.arcgismaps.mapping.symbology.VerticalAlignmentimport com.arcgismaps.mapping.view.Graphicimport com.arcgismaps.mapping.view.GraphicsOverlayimport com.arcgismaps.tasks.geocode.GeocodeParametersimport com.arcgismaps.tasks.geocode.GeocodeResultimport com.arcgismaps.tasks.geocode.LocatorTaskimport com.arcgismaps.toolkit.geoviewcompose.MapViewimport com.arcgismaps.toolkit.geoviewcompose.MapViewProxyimport com.example.app.Rimport kotlinx.coroutines.CoroutineScopeimport kotlinx.coroutines.Jobimport kotlinx.coroutines.launch@Composablefun MainScreen() {val context = LocalContext.currentval coroutineScope = rememberCoroutineScope()// The focus manager is used to dismiss keyboard after search query submission.val focusManager = LocalFocusManager.currentvar 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 linesfun 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 parametersval geocodeParameters = GeocodeParameters().apply {resultAttributeNames.add("*")maxResults = 1outputSpatialReference = currentSpatialReference}// Search for the addresslocatorTask.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 = 8fhaloColor = Color.whitehaloWidth = 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()} -
Call the
SearchBarcomposable, passing the parameters shown below.Pass a lambda for the
onSearchparameter. When theonSearchcallback is invoked, the lambda should clear the focus from the search bar (thus dismissing the device keyboard), then cancel any previous search job, and start a new search job by callingsearchAddress(). Note that theonSearchlambda provides a parameter with the value of the current input query, which you must pass tosearchAddress().MainScreen.kt51 collapsed lines@file:OptIn(ExperimentalMaterial3Api::class)package com.example.app.screensimport android.content.Contextimport android.widget.Toastimport androidx.compose.foundation.layout.Columnimport androidx.compose.foundation.layout.fillMaxSizeimport androidx.compose.foundation.layout.fillMaxWidthimport androidx.compose.foundation.layout.paddingimport androidx.compose.material.icons.Iconsimport androidx.compose.material.icons.filled.Searchimport androidx.compose.material3.ExperimentalMaterial3Apiimport androidx.compose.material3.Iconimport androidx.compose.material3.Scaffoldimport androidx.compose.material3.SearchBarimport androidx.compose.material3.SearchBarDefaultsimport androidx.compose.material3.Textimport androidx.compose.material3.TopAppBarimport androidx.compose.runtime.Composableimport androidx.compose.runtime.getValueimport androidx.compose.runtime.mutableStateOfimport androidx.compose.runtime.rememberimport androidx.compose.runtime.rememberCoroutineScopeimport androidx.compose.runtime.setValueimport androidx.compose.ui.Modifierimport androidx.compose.ui.platform.LocalContextimport androidx.compose.ui.platform.LocalFocusManagerimport androidx.compose.ui.res.stringResourceimport com.arcgismaps.Colorimport com.arcgismaps.geometry.SpatialReferenceimport com.arcgismaps.mapping.ArcGISMapimport com.arcgismaps.mapping.BasemapStyleimport com.arcgismaps.mapping.Viewpointimport com.arcgismaps.mapping.symbology.HorizontalAlignmentimport com.arcgismaps.mapping.symbology.SimpleMarkerSymbolimport com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyleimport com.arcgismaps.mapping.symbology.TextSymbolimport com.arcgismaps.mapping.symbology.VerticalAlignmentimport com.arcgismaps.mapping.view.Graphicimport com.arcgismaps.mapping.view.GraphicsOverlayimport com.arcgismaps.tasks.geocode.GeocodeParametersimport com.arcgismaps.tasks.geocode.GeocodeResultimport com.arcgismaps.tasks.geocode.LocatorTaskimport com.arcgismaps.toolkit.geoviewcompose.MapViewimport com.arcgismaps.toolkit.geoviewcompose.MapViewProxyimport com.example.app.Rimport kotlinx.coroutines.CoroutineScopeimport kotlinx.coroutines.Jobimport kotlinx.coroutines.launch@Composablefun MainScreen() {val context = LocalContext.currentval coroutineScope = rememberCoroutineScope()// The focus manager is used to dismiss keyboard after search query submission.val focusManager = LocalFocusManager.currentvar 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 linesfun 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 parametersval geocodeParameters = GeocodeParameters().apply {resultAttributeNames.add("*")maxResults = 1outputSpatialReference = currentSpatialReference}// Search for the addresslocatorTask.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 = 8fhaloColor = Color.whitehaloWidth = 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 back the
MapView, passing aModifierandmap.MainScreen.kt69 collapsed lines@file:OptIn(ExperimentalMaterial3Api::class)package com.example.app.screensimport android.content.Contextimport android.widget.Toastimport androidx.compose.foundation.layout.Columnimport androidx.compose.foundation.layout.fillMaxSizeimport androidx.compose.foundation.layout.fillMaxWidthimport androidx.compose.foundation.layout.paddingimport androidx.compose.material.icons.Iconsimport androidx.compose.material.icons.filled.Searchimport androidx.compose.material3.ExperimentalMaterial3Apiimport androidx.compose.material3.Iconimport androidx.compose.material3.Scaffoldimport androidx.compose.material3.SearchBarimport androidx.compose.material3.SearchBarDefaultsimport androidx.compose.material3.Textimport androidx.compose.material3.TopAppBarimport androidx.compose.runtime.Composableimport androidx.compose.runtime.getValueimport androidx.compose.runtime.mutableStateOfimport androidx.compose.runtime.rememberimport androidx.compose.runtime.rememberCoroutineScopeimport androidx.compose.runtime.setValueimport androidx.compose.ui.Modifierimport androidx.compose.ui.platform.LocalContextimport androidx.compose.ui.platform.LocalFocusManagerimport androidx.compose.ui.res.stringResourceimport com.arcgismaps.Colorimport com.arcgismaps.geometry.SpatialReferenceimport com.arcgismaps.mapping.ArcGISMapimport com.arcgismaps.mapping.BasemapStyleimport com.arcgismaps.mapping.Viewpointimport com.arcgismaps.mapping.symbology.HorizontalAlignmentimport com.arcgismaps.mapping.symbology.SimpleMarkerSymbolimport com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyleimport com.arcgismaps.mapping.symbology.TextSymbolimport com.arcgismaps.mapping.symbology.VerticalAlignmentimport com.arcgismaps.mapping.view.Graphicimport com.arcgismaps.mapping.view.GraphicsOverlayimport com.arcgismaps.tasks.geocode.GeocodeParametersimport com.arcgismaps.tasks.geocode.GeocodeResultimport com.arcgismaps.tasks.geocode.LocatorTaskimport com.arcgismaps.toolkit.geoviewcompose.MapViewimport com.arcgismaps.toolkit.geoviewcompose.MapViewProxyimport com.example.app.Rimport kotlinx.coroutines.CoroutineScopeimport kotlinx.coroutines.Jobimport kotlinx.coroutines.launch@Composablefun MainScreen() {val context = LocalContext.currentval coroutineScope = rememberCoroutineScope()// The focus manager is used to dismiss keyboard after search query submission.val focusManager = LocalFocusManager.currentvar 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 parametersval geocodeParameters = GeocodeParameters().apply {resultAttributeNames.add("*")maxResults = 1outputSpatialReference = currentSpatialReference}// Search for the addresslocatorTask.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 = 8fhaloColor = Color.whitehaloWidth = 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()} -
Pass these additional parameters to
MapView:graphicsOverlays: Pass thegraphicsOverlaysvariable, defined at the top of theMainScreenblock.mapViewProxy: Pass themapViewProxyvariable (of typeMapViewProxy, defined at the top of theMainScreenblock.onSpatialReferenceChanged: Pass a lambda that retrieves a spatial reference and assigns it to thecurrentSpatialReferencevariable, defined at the top of theMainScreenblock.
MainScreen.kt113 collapsed lines@file:OptIn(ExperimentalMaterial3Api::class)package com.example.app.screensimport android.content.Contextimport android.widget.Toastimport androidx.compose.foundation.layout.Columnimport androidx.compose.foundation.layout.fillMaxSizeimport androidx.compose.foundation.layout.fillMaxWidthimport androidx.compose.foundation.layout.paddingimport androidx.compose.material.icons.Iconsimport androidx.compose.material.icons.filled.Searchimport androidx.compose.material3.ExperimentalMaterial3Apiimport androidx.compose.material3.Iconimport androidx.compose.material3.Scaffoldimport androidx.compose.material3.SearchBarimport androidx.compose.material3.SearchBarDefaultsimport androidx.compose.material3.Textimport androidx.compose.material3.TopAppBarimport androidx.compose.runtime.Composableimport androidx.compose.runtime.getValueimport androidx.compose.runtime.mutableStateOfimport androidx.compose.runtime.rememberimport androidx.compose.runtime.rememberCoroutineScopeimport androidx.compose.runtime.setValueimport androidx.compose.ui.Modifierimport androidx.compose.ui.platform.LocalContextimport androidx.compose.ui.platform.LocalFocusManagerimport androidx.compose.ui.res.stringResourceimport com.arcgismaps.Colorimport com.arcgismaps.geometry.SpatialReferenceimport com.arcgismaps.mapping.ArcGISMapimport com.arcgismaps.mapping.BasemapStyleimport com.arcgismaps.mapping.Viewpointimport com.arcgismaps.mapping.symbology.HorizontalAlignmentimport com.arcgismaps.mapping.symbology.SimpleMarkerSymbolimport com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyleimport com.arcgismaps.mapping.symbology.TextSymbolimport com.arcgismaps.mapping.symbology.VerticalAlignmentimport com.arcgismaps.mapping.view.Graphicimport com.arcgismaps.mapping.view.GraphicsOverlayimport com.arcgismaps.tasks.geocode.GeocodeParametersimport com.arcgismaps.tasks.geocode.GeocodeResultimport com.arcgismaps.tasks.geocode.LocatorTaskimport com.arcgismaps.toolkit.geoviewcompose.MapViewimport com.arcgismaps.toolkit.geoviewcompose.MapViewProxyimport com.example.app.Rimport kotlinx.coroutines.CoroutineScopeimport kotlinx.coroutines.Jobimport kotlinx.coroutines.launch@Composablefun MainScreen() {val context = LocalContext.currentval coroutineScope = rememberCoroutineScope()// The focus manager is used to dismiss keyboard after search query submission.val focusManager = LocalFocusManager.currentvar 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 parametersval geocodeParameters = GeocodeParameters().apply {resultAttributeNames.add("*")maxResults = 1outputSpatialReference = currentSpatialReference}// Search for the addresslocatorTask.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 = 8fhaloColor = Color.whitehaloWidth = 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()} -
(Optional) The code in this tutorial calls a function to display messages to the user. One possible implementation of
showMessage()is the following.MainScreen.kt242 collapsed lines@file:OptIn(ExperimentalMaterial3Api::class)package com.example.app.screensimport android.content.Contextimport android.widget.Toastimport androidx.compose.foundation.layout.Columnimport androidx.compose.foundation.layout.fillMaxSizeimport androidx.compose.foundation.layout.fillMaxWidthimport androidx.compose.foundation.layout.paddingimport androidx.compose.material.icons.Iconsimport androidx.compose.material.icons.filled.Searchimport androidx.compose.material3.ExperimentalMaterial3Apiimport androidx.compose.material3.Iconimport androidx.compose.material3.Scaffoldimport androidx.compose.material3.SearchBarimport androidx.compose.material3.SearchBarDefaultsimport androidx.compose.material3.Textimport androidx.compose.material3.TopAppBarimport androidx.compose.runtime.Composableimport androidx.compose.runtime.getValueimport androidx.compose.runtime.mutableStateOfimport androidx.compose.runtime.rememberimport androidx.compose.runtime.rememberCoroutineScopeimport androidx.compose.runtime.setValueimport androidx.compose.ui.Modifierimport androidx.compose.ui.platform.LocalContextimport androidx.compose.ui.platform.LocalFocusManagerimport androidx.compose.ui.res.stringResourceimport com.arcgismaps.Colorimport com.arcgismaps.geometry.SpatialReferenceimport com.arcgismaps.mapping.ArcGISMapimport com.arcgismaps.mapping.BasemapStyleimport com.arcgismaps.mapping.Viewpointimport com.arcgismaps.mapping.symbology.HorizontalAlignmentimport com.arcgismaps.mapping.symbology.SimpleMarkerSymbolimport com.arcgismaps.mapping.symbology.SimpleMarkerSymbolStyleimport com.arcgismaps.mapping.symbology.TextSymbolimport com.arcgismaps.mapping.symbology.VerticalAlignmentimport com.arcgismaps.mapping.view.Graphicimport com.arcgismaps.mapping.view.GraphicsOverlayimport com.arcgismaps.tasks.geocode.GeocodeParametersimport com.arcgismaps.tasks.geocode.GeocodeResultimport com.arcgismaps.tasks.geocode.LocatorTaskimport com.arcgismaps.toolkit.geoviewcompose.MapViewimport com.arcgismaps.toolkit.geoviewcompose.MapViewProxyimport com.example.app.Rimport kotlinx.coroutines.CoroutineScopeimport kotlinx.coroutines.Jobimport kotlinx.coroutines.launch@Composablefun MainScreen() {val context = LocalContext.currentval coroutineScope = rememberCoroutineScope()// The focus manager is used to dismiss keyboard after search query submission.val focusManager = LocalFocusManager.currentvar 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 parametersval geocodeParameters = GeocodeParameters().apply {resultAttributeNames.add("*")maxResults = 1outputSpatialReference = currentSpatialReference}// Search for the addresslocatorTask.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 = 8fhaloColor = Color.whitehaloWidth = 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()} -
Click Run > Run > app to run the app.
You should see a search box above the map. Search for an address by entering an address and press the magnifying glass on the on the device keyboard. The result of the search should display on the map as a red square.
Alternatively, you can download the tutorial solution, as follows.
Option 2: Download the solution
-
Click the Download solution link in the right-hand side of this page.
-
Unzip the file to a location on your machine.
-
Run Android Studio.
-
Go to File > Open…. Navigate to the solution folder and click Open.
On Windows: If you are in the Welcome to Android Studio dialog, click Open and navigate to the solution folder. Then click Open.
Since the downloaded solution does not contain authentication credentials, you must add the developer credentials that you created in the Set up authentication section.
Set developer credentials in the solution
To allow your app users to access ArcGIS location services
-
In the Android view of Android Studio, open app > kotlin+java > com.example.app > MainActivity. Set the
AuthenticationModeto.API_KEY.MainActivity.kt14 collapsed linespackage com.example.appimport android.os.Bundleimport androidx.activity.ComponentActivityimport androidx.activity.compose.setContentimport androidx.activity.enableEdgeToEdgeimport com.arcgismaps.ApiKeyimport com.arcgismaps.ArcGISEnvironmentimport com.arcgismaps.httpcore.authentication.OAuthUserConfigurationimport com.arcgismaps.toolkit.authentication.AuthenticatorStateimport com.arcgismaps.toolkit.authentication.DialogAuthenticatorimport com.example.app.screens.MainScreenimport com.example.app.ui.theme.TutorialThemeclass MainActivity : ComponentActivity() {private enum class AuthenticationMode { API_KEY, USER_AUTH }private val authenticationMode = AuthenticationMode.API_KEY42 collapsed linesprivate 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)}}}}} -
Set the
apiKeyproperty with your API key access token.MainActivity.kt22 collapsed linespackage com.example.appimport android.os.Bundleimport androidx.activity.ComponentActivityimport androidx.activity.compose.setContentimport androidx.activity.enableEdgeToEdgeimport com.arcgismaps.ApiKeyimport com.arcgismaps.ArcGISEnvironmentimport com.arcgismaps.httpcore.authentication.OAuthUserConfigurationimport com.arcgismaps.toolkit.authentication.AuthenticatorStateimport com.arcgismaps.toolkit.authentication.DialogAuthenticatorimport com.example.app.screens.MainScreenimport com.example.app.ui.theme.TutorialThemeclass MainActivity : ComponentActivity() {private enum class AuthenticationMode { API_KEY, USER_AUTH }private val authenticationMode = AuthenticationMode.API_KEYprivate 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 linesAuthenticationMode.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.
-
In the Android view of Android Studio, open app > kotlin+java > com.example.app > MainActivity. Set the
AuthenticationModeto.USER_AUTH.MainActivity.ktclass MainActivity : ComponentActivity() {private enum class AuthenticationMode { API_KEY, USER_AUTH }private val authenticationMode = AuthenticationMode.USER_AUTH -
Set your
clientIDandredirectURLvalues. You must use the RedirectURL that you supplied for your app in theuser authenticationpart of the Set up authentication step.MainActivity.ktAuthenticationMode.USER_AUTH -> {authenticatorState.oAuthUserConfigurations = listOf(OAuthUserConfiguration(portalUrl = "https://www.arcgis.com",clientId = "YOUR_CLIENT_ID",redirectUrl = "YOUR_REDIRECT_URL")) -
Open app > manifests > AndroidManifest.xml.
-
Set the
android:schemeandandroid:hostusing the scheme and host from your RedirectURL.A redirectURL is composed of a scheme and a host component. The format for the redirect url is
scheme://host. For example, if the redirect url ismyscheme://myhostthen the scheme ismyschemeand the host ismyhost.AndroidManifest.xml41 collapsed lines<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"><uses-permission android:name="android.permission.INTERNET" /><applicationandroid:allowBackup="true"android:dataExtractionRules="@xml/data_extraction_rules"android:fullBackupContent="@xml/backup_rules"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/Theme.Tutorial"tools:targetApi="31"><activityandroid:name=".MainActivity"android:exported="true"android:label="@string/app_name"android:theme="@style/Theme.Tutorial"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity><activityandroid:name="com.arcgismaps.toolkit.authentication.AuthenticationActivity"android:configChanges="keyboard|keyboardHidden|orientation|screenSize"android:exported="true"android:launchMode="singleTop" ><intent-filter><action android:name="android.intent.action.VIEW" /><category android:name="android.intent.category.DEFAULT" /><category android:name="android.intent.category.BROWSABLE" /><dataandroid:scheme="your_redirect_url_scheme"android:host="your_redirect_url_host" />6 collapsed lines</intent-filter></activity></application></manifest>
Best Practice: The OAuth credentials are 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: