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!
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.
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.
Thank you , :)