Eang Tithsophorn
4 min readApr 4, 2023

Android Compose Map and Location permission

I live for mobile development , My name is Eang Tithsophorn. I happy to share , happy to learn …… I hope this is really help your need.

For Map development I am using Google map.

Detail of Usage:

  • Google Map
  • Material3
  • Permission Library
  • Coroutine
  • Flow
  • CallBackFlow

First we need to enable the google map service and get the api key

Please follow the google for how to get the API KEY

List the dependency you need:

implementation("com.google.accompanist:accompanist-permissions:0.23.1")

For google (you don’t need all of that i just copied from doc)

//required
implementation("com.google.maps.android:maps-compose:2.11.2")
//required , Make sure to also include the latest version of the Maps SDK for Android
implementation("com.google.android.gms:play-services-maps:18.0.2")


// Optionally, you can include the Compose utils library for Clustering, etc.
implementation("com.google.maps.android:maps-compose-utils:2.11.2")

// Optionally, you can include the widgets library for ScaleBar, etc.
implementation("com.google.maps.android:maps-compose-widgets:2.11.2")
implementation("com.google.android.gms:play-services-location:20.0.0")

For android Mainfest (For device location , if you dont need that. Just leave it)

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

Ohhh , About the API_KEY for Google Map

<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="API_KEY" />

That’s all for the Setup , Now let’ code to real coding.

val cameraPositionState = rememberCameraPositionState()
GoogleMap(
modifier = Modifier.fillMaxSize(),
cameraPositionState = cameraPositionState,
)

Now That’s work for just map view!

Here we’re goooooooooo!

Now you try to add the latlong for first load!

val cameraPositionState = rememberCameraPositionState {
position = CameraPosition(LatLng(12.1363981, 102.7361403), 10f)
}
//10f is zoom level
//The bigger you put the more it closer

Now We start with Permission (On Load)

You can the permission that you need for that

That is as list of permission that you need

val permissionState = rememberMultiplePermissionsState(
permissions = listOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
)
)

and this is a single permission

val permissionState = rememberPermissionState(permission = Manifest.permission.ACCESS_FINE_LOCATION)

Here the On the Run code

PermissionsRequired(
multiplePermissionsState = permissionState,
permissionsNotGrantedContent = { /*TODO*/ },
permissionsNotAvailableContent = { /*TODO*/ }) {
// the content where it granted
}

All combine as one compose function

@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun CheckPermission() {
val cameraPositionState = rememberCameraPositionState()
val permissionState = rememberMultiplePermissionsState(
permissions = listOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
)
)
val isPermissionGranted = permissionState.allPermissionsGranted

PermissionsRequired(
multiplePermissionsState = permissionState,
permissionsNotGrantedContent = {
LaunchedEffect(isPermissionGranted) {
permissionState.launchMultiplePermissionRequest()
}
},
permissionsNotAvailableContent = {
GoogleMap(
modifier = Modifier.fillMaxSize(),
cameraPositionState = cameraPositionState,
properties = MapProperties(isMyLocationEnabled = isPermissionGranted),
)
}) {
GoogleMap(
modifier = Modifier.fillMaxSize(),
cameraPositionState = cameraPositionState,
properties = MapProperties(isMyLocationEnabled = isPermissionGranted),
)
}
}

Here The full result.

Let’s goooooo.

You may notice that you have to click on my location to navigate to it right?

Since you don’t set it to navigate. it doesn’t know where you are!

Let’s me guide you a bit more!

Create a Kotlin function not a Compose

fun navigateToCurrentLocation(context: Context): Flow<Pair<Double?, Double?>> {
return callbackFlow {
val fusedLocationClient = LocationServices.getFusedLocationProviderClient(context)
if (ActivityCompat.checkSelfPermission(
context,
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
context,
Manifest.permission.ACCESS_COARSE_LOCATION
) == PackageManager.PERMISSION_GRANTED
) {
fusedLocationClient.lastLocation.addOnSuccessListener { location: Location? ->
val param = Pair(location?.latitude, location?.longitude)
trySend(param)
}
}
awaitClose { close() }
}
}

This line is where we get current user location

val fusedLocationClient = LocationServices.getFusedLocationProviderClient(context)

But thank Google team with a nicely work. We know when did Map finish the load with onMapLoaded

GoogleMap(
modifier = Modifier.fillMaxSize(),
cameraPositionState = cameraPositionState,
properties = MapProperties(isMyLocationEnabled = isPermissionGranted),
onMapLoaded = {
if (isPermissionGranted) {
val result = navigateToCurrentLocation(context)
coroutineScope.launch {
result.collect {
cameraPositionState.animate(
update = CameraUpdateFactory.newCameraPosition(
CameraPosition(
LatLng(
(it.first ?: 0) as Double,
(it.second ?: 0) as Double
), 15f, 0f, 0f
)
),
durationMs = 1000
)
}
}
}
},
)

Why CallBackFlow?

CallBackFlow it more like a Kotlin style and for get current location

it call back from Task of location (Task<Location>)

Since Flow it about subspend . So this case i will use coroutine as it already show.

Here the full code for you.

@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun LocationScreen() {
val cameraPositionState = rememberCameraPositionState()
val permissionState = rememberMultiplePermissionsState(
permissions = listOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
)
)
val context = LocalContext.current
val coroutineScope = rememberCoroutineScope()
val isPermissionGranted = permissionState.allPermissionsGranted

PermissionsRequired(
multiplePermissionsState = permissionState,
permissionsNotGrantedContent = {
LaunchedEffect(isPermissionGranted) {
permissionState.launchMultiplePermissionRequest()
}
},
permissionsNotAvailableContent = {
GoogleMap(
modifier = Modifier.fillMaxSize(),
cameraPositionState = cameraPositionState,
properties = MapProperties(isMyLocationEnabled = isPermissionGranted),
)
}) {
GoogleMap(
modifier = Modifier.fillMaxSize(),
cameraPositionState = cameraPositionState,
properties = MapProperties(isMyLocationEnabled = isPermissionGranted),
onMapLoaded = {
if (isPermissionGranted) {
val result = navigateToCurrentLocation(context)
coroutineScope.launch {
result.collect {
cameraPositionState.animate(
update = CameraUpdateFactory.newCameraPosition(
CameraPosition(
LatLng(
(it.first ?: 0) as Double,
(it.second ?: 0) as Double
), 15f, 0f, 0f
)
),
durationMs = 1000
)
}
}
}
},
)
}
}

That’s work with animation already.

Here another way

cameraPositionState.position = CameraPosition(
LatLng(
(it.first ?: 0) as Double,
(it.second ?: 0) as Double
), 15f, 0f, 0f
)

This way doesn’t have animate.

That’s all you need to know for permission and device location.

I hope it help your need.

Here my Git and My site.

Thank you , :)

Eang Tithsophorn
Eang Tithsophorn

Written by Eang Tithsophorn

I am Mobile Development Learner. My mobile list box has Android , iOS and Flutter. More information http://128.199.87.161/

No responses yet