Location permissions Android KMP CMP
rememberPermissionState
from Accompanist, while for Compose Multiplatform, moko-permissions provides a unified API for Android and iOS.Hey there, fellow developers! Today, we’re diving into how to handle location permissions in Kotlin, focusing on both Android with Jetpack Compose and cross-platform with Compose Multiplatform. Whether you’re building a navigation app or just need to know where users are, getting location permissions right is key. Let’s break it down step by step.
For Android apps using Jetpack Compose, managing location permissions is straightforward with the Accompanist Permissions library. First, add this to your build.gradle
:
implementation "com.google.accompanist:accompanist-permissions:0.37.2"
Then, in your composable, use rememberPermissionState
to request the fine location permission. Here’s a quick example:
import androidx.compose.runtime.Composable
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.rememberPermissionState
import android.Manifest
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun LocationPermissionScreen() {
val permissionState = rememberPermissionState(
permission = Manifest.permission.ACCESS_FINE_LOCATION
)
when {
permissionState.hasPermission -> Text("Location permission granted")
permissionState.shouldShowRationale -> {
Text("We need location for this feature. Please grant it.")
Button(onClick = { permissionState.launchPermissionRequest() }) {
Text("Request permission")
}
}
else -> Text("Permission denied. Enable it in settings.")
}
}
Don’t forget to add <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
to your AndroidManifest.xml
.
For cross-platform apps using Compose Multiplatform, moko-permissions is a game-changer. It handles permissions on both Android and iOS with a unified API. Add these to your shared module’s build.gradle
:
commonMainImplementation("dev.icerock.moko:permissions:0.19.1")
commonMainImplementation("dev.icerock.moko:permissions-compose:0.19.1")
In your platform code, like MainActivity
for Android, bind the PermissionsController
:
class MainActivity : ComponentActivity() {
private val permissionsController = PermissionsController()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
permissionsController.bind(this)
setContent {
App(permissionsController)
}
}
}
Then, in your shared composable, request the permission:
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import dev.icerock.moko.permissions.PermissionsController
import dev.icerock.moko.permissions.Permissions
import kotlinx.coroutines.launch
@Composable
fun LocationPermissionCMP(permissionsController: PermissionsController) {
val scope = rememberCoroutineScope()
val permissionState = remember { mutableStateOf(false) }
LaunchedEffect(Unit) {
if (permissionsController.isPermissionGranted(Permissions.FINE_LOCATION)) {
permissionState.value = true
}
}
if (permissionState.value) {
Text("Location permission granted")
} else {
Button(onClick = {
scope.launch {
try {
permissionsController.providePermission(Permissions.FINE_LOCATION)
permissionState.value = true
} catch (e: Exception) {
println("Permission denied: $e")
}
}
}) {
Text("Request Location Permission")
}
}
}
For iOS, moko-permissions handles the binding internally, and you need to add NSLocationWhenInUseUsageDescription
to your Info.plist
.
Welcome to a comprehensive exploration of implementing location permission handling in Kotlin, focusing on Jetpack Compose for Android and Compose Multiplatform (CMP) with moko-permissions. This analysis aims to provide detailed guidance on setting up these mechanisms using the latest library versions, ensuring compatibility with Kotlin 2.1.20, Compose Multiplatform 1.7.0, and related dependencies, informed by official documentation and community resources, aligning with EEAT principles for credibility and engagement.
Location permissions are essential for mobile applications requiring access to user location data, such as navigation, weather updates, or location-based services. Handling these permissions correctly is crucial for functionality and user privacy, particularly in cross-platform development. This note explores how to implement location permission handling in Kotlin, covering Android with Jetpack Compose using Accompanist Permissions and CMP with moko-permissions, considering the current date and ensuring compatibility with recent updates.
The latest versions, based on current research, are Kotlin 2.1.20 (Kotlin releases | Kotlin Documentation), Compose Multiplatform 1.7.0 (Releases · JetBrains/compose-multiplatform), Accompanist Permissions 0.37.2 (Releases · google/accompanist), and moko-permissions 0.19.1 (Maven Central: dev.icerock.moko:permissions-compose). These versions ensure compatibility with the current development environment.
Location permissions vary by platform. In Android, permissions include ACCESS_FINE_LOCATION for precise location and ACCESS_COARSE_LOCATION for approximate location, with additional considerations for background location since Android 10. iOS uses CLLocationManager for requesting “When In Use” or “Always” location access, requiring entries in Info.plist like NSLocationWhenInUseUsageDescription. For CMP, handling these platform-specific permissions requires a unified approach, often through libraries like moko-permissions, which abstracts the differences.
An unexpected detail is that moko-permissions automatically handles iOS permission binding, simplifying the process compared to manual implementation, which would require platform-specific code for each target.
For Android apps using Jetpack Compose, the Accompanist Permissions library provides a reactive way to handle runtime permissions. The library, part of Google’s Accompanist project, is experimental but widely used, with version 0.37.2 being the latest (Releases · google/accompanist).
To implement, add the dependency:
dependencies {
implementation "com.google.accompanist:accompanist-permissions:0.37.2"
}
Use rememberPermissionState
for single permissions or rememberMultiplePermissionsState
for multiple, as shown:
import androidx.compose.runtime.Composable
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.rememberPermissionState
import android.Manifest
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun LocationPermissionScreen() {
val permissionState = rememberPermissionState(
permission = Manifest.permission.ACCESS_FINE_LOCATION
)
when {
permissionState.hasPermission -> {
// Permission is granted, proceed with location access
Text("Location permission granted")
}
permissionState.shouldShowRationale -> {
// Show rationale and request permission
Text("Location permission is needed for this feature. Please grant the permission.")
Button(onClick = { permissionState.launchPermissionRequest() }) {
Text("Request permission")
}
}
!permissionState.hasPermission && !permissionState.shouldShowRationale -> {
// Permission denied permanently, guide user to settings
Text("Location permission was denied. Please enable it in settings.")
}
}
}
Ensure to declare the permission in AndroidManifest.xml
:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
This approach is Android-specific, with no direct support for KMP, but it’s robust for Android development, leveraging Compose’s declarative UI.
For CMP, which extends Jetpack Compose to multiple platforms, handling permissions requires a multiplatform library. moko-permissions, developed by Icerock, provides runtime permissions control for Android and iOS, with version 0.19.1 (Maven Central: dev.icerock.moko:permissions-compose).
Add the dependencies:
dependencies {
commonMainImplementation("dev.icerock.moko:permissions:0.19.1")
commonMainImplementation("dev.icerock.moko:permissions-compose:0.19.1")
}
The implementation involves creating a PermissionsController
and binding it to the platform lifecycle. For Android, in MainActivity
:
class MainActivity : ComponentActivity() {
private val permissionsController = PermissionsController()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
permissionsController.bind(this)
setContent {
App(permissionsController)
}
}
}
For iOS, moko-permissions handles the binding internally, requiring NSLocationWhenInUseUsageDescription
in Info.plist
:
<key>NSLocationWhenInUseUsageDescription</key>
<string>Needed for location-based features</string>
In the shared composable, use the controller to request permissions:
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import dev.icerock.moko.permissions.PermissionsController
import dev.icerock.moko.permissions.Permissions
import kotlinx.coroutines.launch
@Composable
fun LocationPermissionCMP(permissionsController: PermissionsController) {
val scope = rememberCoroutineScope()
val permissionState = remember { mutableStateOf(false) }
LaunchedEffect(Unit) {
if (permissionsController.isPermissionGranted(Permissions.FINE_LOCATION)) {
permissionState.value = true
}
}
if (permissionState.value) {
Text("Location permission granted")
} else {
Button(onClick = {
scope.launch {
try {
permissionsController.providePermission(Permissions.FINE_LOCATION)
permissionState.value = true
} catch (e: Exception) {
// Handle denial
println("Permission denied: $e")
}
}
}) {
Text("Request Location Permission")
}
}
}
This approach leverages CMP’s shared UI, with moko-permissions handling platform-specific details, making it ideal for cross-platform development.
To facilitate comparison, here’s a table summarizing the approaches:
Aspect | Android with Accompanist | Compose Multiplatform with moko-permissions |
---|---|---|
Dependency | com.google.accompanist:accompanist-permissions:0.37.2 | dev.icerock.moko:permissions:0.19.1 , dev.icerock.moko:permissions-compose:0.19.1 |
Platform Support | Android only | Android, iOS (with potential for desktop) |
Permission Declaration | AndroidManifest.xml | AndroidManifest.xml for Android, Info.plist for iOS |
Implementation | rememberPermissionState | PermissionsController with platform binding |
Multiplatform Ready | No | Yes |
Best practices include:
isPermissionGranted
in moko-permissions or hasPermission
in Accompanist.This detailed exploration shows how to implement location permission handling in Kotlin using Jetpack Compose for Android with Accompanist Permissions and in CMP with moko-permissions. Each approach has its strengths: Accompanist for Android robustness, and moko-permissions for cross-platform simplicity, especially with its automatic iOS handling. Personally, I find moko-permissions a lifesaver for CMP projects, as it abstracts the platform differences, making development smoother. What do you think? Share your thoughts in the comments, and let’s discuss!
Key Citations:
Introduction: Transform Your Cross-Platform Development with Material Design 3 Are you ready to revolutionize your… Read More
Jetpack Compose 1.8 rolls out handy features like Autofill integration, slick Text enhancements including auto-sizing… Read More
Reified Keyword in Kotlin: Simplify Your Generic Functions Kotlin's reified keyword lets your generic functions know the… Read More
Android Studio Cloud: Ditch the Setup, Code Anywhere (Seriously!) Alright, fellow Android devs, gather 'round… Read More