Learn how to display the current device location on a map or scene.

You can display the device location on a map or scene. This is important for workflows that require the user's current location, such as finding nearby businesses, navigating from the current location, or identifying and collecting geospatial information.
By default, location display uses the device's location provider. Your app can also process input from other location providers, such as an external GPS receiver or a provider that returns a simulated location. For more information, see the Show device location topic.
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.
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 display the current device location on a map or scene.
-
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
element, change the text content to Display device location._name" > strings.xmlUse dark colors for code blocks <resources> <string name="app_name">Display device location</string> </resources>
-
In the Android view, open Gradle Scripts > settings.gradle.kts.
Change the value of
root
to "Display device location".Project.name settings.gradle.ktsUse dark colors for code blocks dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral() maven { url = uri("https://esri.jfrog.io/artifactory/arcgis") } } } rootProject.name = "Display device location" include(":app")
-
Click File > Sync Project with Gradle files. Android Studio will recognize your changes and create a new .idea folder.
-
Add import statements
-
In the Android view, open app > kotlin+java > com.example.app > MainScreen.kt. Replace the import statements with the imports needed for this tutorial.
MainScreen.ktUse dark colors for code blocks @file:OptIn(ExperimentalMaterial3Api::class) package com.example.app.screens import android.Manifest import android.content.Context import android.content.pm.PackageManager import android.widget.Toast import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.core.content.ContextCompat import com.arcgismaps.ArcGISEnvironment import com.arcgismaps.location.LocationDisplayAutoPanMode import com.arcgismaps.mapping.ArcGISMap import com.arcgismaps.mapping.BasemapStyle import com.arcgismaps.mapping.Viewpoint import com.arcgismaps.toolkit.geoviewcompose.MapView import com.arcgismaps.toolkit.geoviewcompose.rememberLocationDisplay import com.example.app.R import kotlinx.coroutines.launch
Check location permissions
-
In the Android view, open app > manifests > AndroidManifest.xml.
In AndroidManifest.xml, add permissions for coarse and fine location.
AndroidManifest.xmlUse dark colors for code blocks <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
-
In MainScreen.kt, create a top-level function named
check
to check whether both coarse and fine location permissions are granted. This function takes the current localPermissions() Context
as a parameter, and returnstrue
if both permissions are granted andfalse
otherwise.MainScreen.ktUse dark colors for code blocks fun checkPermissions(context: Context): Boolean { // Check permissions to see if both permissions are granted. // Coarse location permission. val permissionCheckCoarseLocation = ContextCompat.checkSelfPermission( context, Manifest.permission.ACCESS_COARSE_LOCATION ) == PackageManager.PERMISSION_GRANTED // Fine location permission. val permissionCheckFineLocation = ContextCompat.checkSelfPermission( context, Manifest.permission.ACCESS_FINE_LOCATION ) == PackageManager.PERMISSION_GRANTED return permissionCheckCoarseLocation && permissionCheckFineLocation }
Show device location if permissions were granted
-
In the
Main
composable, you will add code that creates a location display and sets the auto pan mode.Screen Get the local
Context
and use it to set theArcGISEnvironment.applicationContext
property.MainScreen.ktUse dark colors for code blocks @Composable fun MainScreen() { val context = LocalContext.current val coroutineScope = rememberCoroutineScope() ArcGISEnvironment.applicationContext = context.applicationContext val map = remember { createMap() } Scaffold( topBar = { TopAppBar(title = { Text(text = stringResource(id = R.string.app_name)) }) } ) { MapView( modifier = Modifier.fillMaxSize().padding(it), arcGISMap = map, ) } }
-
Create a
LocationDisplay
by calling the composable functionrememberLocationDisplay()
defined in the ArcGIS Maps SDK for Kotlin Toolkit. Then set aLocationDisplayAutoPanMode
that centers the map at the device location.MainScreen.ktUse dark colors for code blocks @Composable fun MainScreen() { val context = LocalContext.current val coroutineScope = rememberCoroutineScope() ArcGISEnvironment.applicationContext = context.applicationContext // Create and remember a location display with a recenter auto pan mode. val locationDisplay = rememberLocationDisplay().apply { setAutoPanMode(LocationDisplayAutoPanMode.Recenter) } val map = remember { createMap() } Scaffold( topBar = { TopAppBar(title = { Text(text = stringResource(id = R.string.app_name)) }) } ) { MapView( modifier = Modifier.fillMaxSize().padding(it), arcGISMap = map, ) } }
-
Set the
location
property ofDisplay MapView
usinglocation
, which we just created.Display MainScreen.ktUse dark colors for code blocks @Composable fun MainScreen() { val context = LocalContext.current val coroutineScope = rememberCoroutineScope() ArcGISEnvironment.applicationContext = context.applicationContext // Create and remember a location display with a recenter auto pan mode. val locationDisplay = rememberLocationDisplay().apply { setAutoPanMode(LocationDisplayAutoPanMode.Recenter) } val map = remember { createMap() } Scaffold( topBar = { TopAppBar(title = { Text(text = stringResource(id = R.string.app_name)) }) } ) { MapView( modifier = Modifier.fillMaxSize().padding(it), arcGISMap = map, locationDisplay = locationDisplay ) } }
-
You will start location display, provided both permissions were granted.
To do so, call
check
, which returns true if both coarse and fine location permissions are granted. If the return value is true, call thePermissions() Launched
composable. In theEffect Launched
block, callEffect start()
on the data source for the location display.MainScreen.ktUse dark colors for code blocks @Composable fun MainScreen() { val context = LocalContext.current val coroutineScope = rememberCoroutineScope() ArcGISEnvironment.applicationContext = context.applicationContext // Create and remember a location display with a recenter auto pan mode. val locationDisplay = rememberLocationDisplay().apply { setAutoPanMode(LocationDisplayAutoPanMode.Recenter) } if (checkPermissions(context)) { // Permissions are already granted. LaunchedEffect(Unit) { locationDisplay.dataSource.start() } } else { } val map = remember { createMap() } Scaffold( topBar = { TopAppBar(title = { Text(text = stringResource(id = R.string.app_name)) }) } ) { MapView( modifier = Modifier.fillMaxSize().padding(it), arcGISMap = map, locationDisplay = locationDisplay ) } }
Request location permissions if they were not granted
-
Create a composable function named
Request
that takes two parameters: one namedPermissions context
and the other namedon
, which is a function to be executed after requested permissions have been granted.Permissions Granted MainScreen.ktUse dark colors for code blocks @Composable fun RequestPermissions(context: Context, onPermissionsGranted: () -> Unit) { }
-
In
Request
, create anPermissions Activity
by calling the composable functionResult Launcher remember
.Launcher For Activity Result() Check that the value for each map entry in
permissions
is true. If both values are true, call theon
callback function. If values are not both true, show an error message.Permissions Granted You can show an error message in various ways. The code in this tutorial calls a custom function named
show
, which is defined like this:Error() MainScreen.ktUse dark colors for code blocks fun showError(context: Context, message: String) { Toast.makeText(context, message, Toast.LENGTH_LONG).show() }
MainScreen.ktUse dark colors for code blocks @Composable fun RequestPermissions(context: Context, onPermissionsGranted: () -> Unit) { // Create an activity result launcher using permissions contract and handle the result. val activityResultLauncher = rememberLauncherForActivityResult( ActivityResultContracts.RequestMultiplePermissions() ) { permissions -> // Check if both fine & coarse location permissions are true. if (permissions.all { it.value }) { onPermissionsGranted() } else { showError(context, "Location permissions were denied") } } }
-
Call the
Launched
composable. In theEffect Launched
block, execute the activity result launcher by callingEffect activity
and passing the permissions you are requesting: fine and coarse location permissions.Result Launcher.launch() MainScreen.ktUse dark colors for code blocks @Composable fun RequestPermissions(context: Context, onPermissionsGranted: () -> Unit) { // Create an activity result launcher using permissions contract and handle the result. val activityResultLauncher = rememberLauncherForActivityResult( ActivityResultContracts.RequestMultiplePermissions() ) { permissions -> // Check if both fine & coarse location permissions are true. if (permissions.all { it.value }) { onPermissionsGranted() } else { showError(context, "Location permissions were denied") } } LaunchedEffect(Unit) { activityResultLauncher.launch( // Request both fine and coarse location permissions. arrayOf( Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION ) ) } }
-
Last, in the
Main
composable, find the code that checks if coarse and fine location permissions were both granted.Screen MainScreen.ktUse dark colors for code blocks Copy @Composable fun MainScreen() { val context = LocalContext.current val coroutineScope = rememberCoroutineScope() ArcGISEnvironment.applicationContext = context.applicationContext // Create and remember a location display with a recenter auto pan mode. val locationDisplay = rememberLocationDisplay().apply { setAutoPanMode(LocationDisplayAutoPanMode.Recenter) } if (checkPermissions(context)) { // Permissions are already granted. LaunchedEffect(Unit) { locationDisplay.dataSource.start() } } else { } val map = remember { createMap() } Scaffold( topBar = { TopAppBar(title = { Text(text = stringResource(id = R.string.app_name)) }) } ) { MapView( modifier = Modifier.fillMaxSize().padding(it), arcGISMap = map, locationDisplay = locationDisplay ) } }
-
In the
else
block (permissions were not granted), callRequest
and pass a trailing lambda as thePermissions on
parameter. The lambda will be called after the permissions are granted. In the lambda, start the data source of the location display.Permission Granted Since
Location
is aData Source.start() suspend
function, we call it from aCoroutine
block.Scope.launch MainScreen.ktUse dark colors for code blocks @Composable fun MainScreen() { val context = LocalContext.current val coroutineScope = rememberCoroutineScope() ArcGISEnvironment.applicationContext = context.applicationContext // Create and remember a location display with a recenter auto pan mode. val locationDisplay = rememberLocationDisplay().apply { setAutoPanMode(LocationDisplayAutoPanMode.Recenter) } if (checkPermissions(context)) { // Permissions are already granted. LaunchedEffect(Unit) { locationDisplay.dataSource.start() } } else { RequestPermissions( context = context, onPermissionsGranted = { coroutineScope.launch { locationDisplay.dataSource.start() } } ) } val map = remember { createMap() } Scaffold( topBar = { TopAppBar(title = { Text(text = stringResource(id = R.string.app_name)) }) } ) { MapView( modifier = Modifier.fillMaxSize().padding(it), arcGISMap = map, locationDisplay = locationDisplay ) } }
Run your app
Click Run > Run > app to run the app.
In Android Studio, you have two choices for running your app: an actual Android device or the Android Emulator.
Android device
Connect your computer to your Android device, using USB or Wi-Fi. For more details, see How to connect your Android device.
Android Emulator
Create an AVD (Android Virtual Device) to run in the Android Emulator. For details, see Run apps on the Android Emulator.
Selecting a device
When you build and run an app in Android Studio, you must first select a device. From the Android Studio toolbar, you can access the drop-down list of your currently available devices, both virtual and physical.
.
If you cannot access the list on the toolbar, click Tools > Device Manager.
The app should display a permissions popup, in which you must tap Precise
(for fine permissions) or Approximate
(for coarse permissions). After you do so, your current location should display on the map. Different location symbols are used depending on the auto pan mode and whether a location is acquired. See LocationDisplayAutoPanMode
for details.
By default, a round blue symbol is used to display the device's location. The location data source tries to get the most accurate location available but depending upon signal strength, satellite positions, and other factors, the location reported could be an approximation. A semi-transparent circle around the location symbol indicates the range of accuracy. As the device moves and location updates are received, the location symbol will be repositioned on the map.
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 first set up authentication to create credentials, and then add the developer credentials to the solution.
Set up authentication
To access the secure ArcGIS location services used in this tutorial, you must implement API key authentication or user authentication using an ArcGIS Location Platform or an ArcGIS Online account.
You can implement API key authentication or user authentication in this tutorial. Compare the differences below:
API key authentication
- Users are not required to sign in.
- Requires creating an API key credential with the correct privileges.
- API keys are long-lived access tokens.
- Service usage is billed to the API key owner/developer.
- Simplest authentication method to implement.
- Recommended approach for new ArcGIS developers.
Learn more in API key authentication.
User authentication
- Users are required to sign in with an ArcGIS account.
- User accounts must have privilege to access the ArcGIS services used in application.
- Requires creating OAuth credentials.
- Application uses a redirect URL and client ID.
- Service usage is billed to the organization of the user signed into the application.
Learn more in User authentication.
Create a new API key access token with privileges to access the secure resources used in this tutorial.
-
Complete the Create an API key tutorial and create an API key with the following privilege(s):
- Privileges
- Location services > Basemaps
- Privileges
-
Copy and paste the API key access token into a safe location. It will be used in a later step.
Set developer credentials in the solution
To allow your app users to access ArcGIS location services, use the developer credentials that you created in the Set up authentication step to authenticate requests for resources.
-
In the Android view of Android Studio, open app > kotlin+java > com.example.app > MainActivity. Set the
Authentication
toMode .
.API _KEY MainActivity.ktUse dark colors for code blocks class MainActivity : ComponentActivity() { private enum class AuthenticationMode { API_KEY, USER_AUTH } private val authenticationMode = AuthenticationMode.API_KEY
-
Set the
api
property with your API key access token.Key MainActivity.ktUse dark colors for code blocks override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) when (authenticationMode) { AuthenticationMode.API_KEY -> { ArcGISEnvironment.apiKey = ApiKey.create("YOUR_ACCESS_TOKEN") }
Best Practice: The access token is stored directly in the code as a convenience for this tutorial. Do not store credentials directly in source code in a production environment.
Run the app
Click Run > Run > app to run the app.
In Android Studio, you have two choices for running your app: an actual Android device or the Android Emulator.
Android device
Connect your computer to your Android device, using USB or Wi-Fi. For more details, see How to connect your Android device.
Android Emulator
Create an AVD (Android Virtual Device) to run in the Android Emulator. For details, see Run apps on the Android Emulator.
Selecting a device
When you build and run an app in Android Studio, you must first select a device. From the Android Studio toolbar, you can access the drop-down list of your currently available devices, both virtual and physical.
.
If you cannot access the list on the toolbar, click Tools > Device Manager.
The app should display a permissions popup, in which you must tap Precise
(for fine permissions) or Approximate
(for coarse permissions). After you do so, your current location should display on the map. Different location symbols are used depending on the auto pan mode and whether a location is acquired. See LocationDisplayAutoPanMode
for details.
By default, a round blue symbol is used to display the device's location. The location data source tries to get the most accurate location available but depending upon signal strength, satellite positions, and other factors, the location reported could be an approximation. A semi-transparent circle around the location symbol indicates the range of accuracy. As the device moves and location updates are received, the location symbol will be repositioned on the map.
What's next?
Learn how to use additional API features, ArcGIS location services, and ArcGIS tools in these tutorials: