Let's think about it this way for a second
Just declaring a permission in AndroidManifest.xml isn't enough — for sensitive permissions (Camera, Location, Notification), you also have to show a dialog and ask the user directly at app runtime (Android 6.0+). The user can pick 'Allow' or 'Deny' — if they pick 'Deny', you need to handle the feature gracefully (so the app doesn't crash).
Let's connect this to a real scenario
For an app with a camera feature, add `<uses-permission android:name="android.permission.CAMERA" />` to `AndroidManifest.xml`, then use `rememberLauncherForActivityResult` in Compose to trigger the runtime permission dialog — you can only enable the camera feature once the user taps 'Allow'; if they pick 'Deny', show an explanation message and keep the feature disabled.
Let's walk through it together
@Composable
fun CameraScreen() {
var hasPermission by remember { mutableStateOf(false) }
val launcher = rememberLauncherForActivityResult(
ActivityResultContracts.RequestPermission()
) { granted -> hasPermission = granted }
Button(onClick = { launcher.launch(Manifest.permission.CAMERA) }) {
Text("Request Camera Permission")
}
if (hasPermission) {
Text("Camera ready!")
} else {
Text("Camera permission not granted")
}
}Clicking the button should bring up the system permission dialog, and picking 'Allow' should show 'Camera ready!'5-minute try-it
Run `CameraScreen` and click the permission button — try both 'Allow' and 'Deny' and see how the UI changes each time.
A quick word of caution
If the user keeps picking 'Deny' (or taps 'Don't ask again'), Android stops showing the permission dialog altogether — since they'd have to go into Settings manually at that point, your app should handle this scenario too.