Runtime Permissions
ActivityResult API & rememberLauncherForActivityResult
Dangerous permissions (camera, location, notifications) must be requested at runtime and can be revoked anytime. The modern way is the `ActivityResult` contract, which you launch from a Composable.
๐ Explanation
Runtime permissions use the `ActivityResult` API instead of the old `onRequestPermissionsResult` callback. `rememberLauncherForActivityResult` registers a launcher with the `RequestPermission()` contract and survives recomposition; calling `launcher.launch(...)` shows the system dialog and delivers the boolean result to your lambda. Always re-check with `checkSelfPermission` because the user can revoke a grant from Settings while your app is in the background โ never assume a one-time grant is permanent.
Note: Code execution is not available for Android/Kotlin in the browser. Use Android Studio to run and test the app.
import android.Manifestimport android.content.pm.PackageManagerimport androidx.activity.compose.rememberLauncherForActivityResultimport androidx.activity.result.contract.ActivityResultContractsimport androidx.compose.material3.*import androidx.compose.runtime.*import androidx.compose.ui.platform.LocalContextimport androidx.core.content.ContextCompat@Composablefun CameraPermissionGate(onGranted: () -> Unit) {val context = LocalContext.current// Re-check current grant state each compositionvar hasPermission by remember {mutableStateOf(ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED)}// 1. Register a launcher tied to the permission contractval launcher = rememberLauncherForActivityResult(contract = ActivityResultContracts.RequestPermission()) { granted ->hasPermission = grantedif (granted) onGranted()}if (hasPermission) {Text("Camera ready")} else {Button(onClick = {// 2. Launching shows the system dialoglauncher.launch(Manifest.permission.CAMERA)}) {Text("Grant camera access")}}}