Hello everyone!
Today we will be talking about permission handling in android jetpack compose using google’s accompanist library.
We will discuss various use cases and permission handling scenarios in this article. By the end of it all, you will have everything with you to manage permissions in your android app.
The entire code discussed can be found in the repository given below.
Here is a little preview of what we will be building:

If you don’t want to use Accompanist for adding requesting permission, you can refer to my another article.
We will also discuss few use cases that you might encounter while handling permissions in the app after completing the implementation of permissions.
Dependencies
implementation "com.google.accompanist:accompanist-permissions:0.21.1-beta"
Note: The Accompanist library is new and experimental. There might be changes introduced in the future that may make the code erroneous.
1. Adding permissions to Manifest file
Android provides you with various permissions to choose from and to add to your application. You can find them all here.
Since we will be working with camera permission, add the following code to your android manifest file.
<uses-permission android:name="android.permission.CAMERA"/>
<uses-feature android:name="android.hardware.camera.any" />
2. Accessing the Camera
Now let us bring our accompanist library to use and access our camera permission.
Given below, we have created a composable cameraPermissionState( ), where
@Composable
fun cameraPermissionState(): PermissionState {
return rememberPermissionState(permission = CAMERA_PERMISSION)
}
This composable returns the state of our camera permission, whether the system has granted the camera permission or not.
rememberPermissionState( ) is a public function provided by the accompanist library which maintains our permission state throughout.
Note: rememberPermissionState( ) is still an experimental api. Make sure you add @OptIn(ExperimentalPermissionsApi::class) to our composable HomeScreen( ).
Therefore code looks like
@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun cameraPermissionState(): PermissionState {
return rememberPermissionState(permission = CAMERA_PERMISSION)
}
Additionally
In case of multiple permissions, you can use rememberMultiplePermissionState( ) which functions the same as rememberPermissionState( ), but for multiple permissions. Do not forget to add @OptIn(ExperimentalPermissionsApi::class) annotation for the same.
val totalPermissions = rememberMultiplePermissionsState(
permissions = listOf(
Manifest.permission.CAMERA,
Manifest.permission.ACCESS_FINE_LOCATION
)
)
3. Loading the image
After adding the composable to return our camera permission. Let us now add some variables to fetch our captured image to the screen.
Here, we have created another composable ProfileScreenWithAccompanist( ), where we build our profile screen. Variables are added below.
@Composable
fun ProfileScreenWithAccompanist() {
var resultBitmap: Bitmap? by rememberSaveable { mutableStateOf(placeHolderBitmap) }
val launcherForImageCapture = rememberLauncherForActivityResult(
contract = ActivityResultContracts.TakePicturePreview()
) {
resultBitmap = if (it.toString().isEmpty()) {
placeHolderBitmap
} else {
it
}
}
}
Here,
1. resultBitmap is our variable that stores the image captured in Bitmap.
2. Another variable launcherForImageCapture launches the camera to capture the image.
Note: placeHolderBitmap is a variable that is used as a place holder before the image is clicked.
val placeHolderBitmap: Bitmap = BitmapFactory.decodeResource(
Resources.getSystem(),
android.R.drawable.ic_menu_camera
)
4. Adding the permissions logic
Now, let us add our permission handling logic to an Image.
Image(
bitmap = it,
contentDescription = "Captured image",
contentScale = ContentScale.Crop,
modifier = Modifier
.size(150.dp)
.clickable {
if (cameraPermission.hasPermission) {
launcherForImageCapture.launch()
} else if (!cameraPermission.hasPermission) {
cameraPermission.launchPermissionRequest()
}
}
)
Since the state of the permission persists in the cameraPermission which uses rememberPermission function, it provides the following features
permission | The permission to control and observe |
hasPermission | It is a boolean. When true, the user has granted permission |
permissionRequested | It is a boolean property. When true, the permission request has been done previously |
shouldShowRationale | Again a boolean property. When true, the user should be presented with a rationale |
launchPermissionRequest( ) | This triggers a system dialog that asks the user to grant or revoke the permission |
Here, as the icon button is clicked, it checks if the camera permission is granted.
1. If yes, then it launches the camera and loads the image.
2. If the permission is not granted, then it launches a permission request using launchPermissionRequest( ).

5. Finishing up
Finally, with all the logic in our hands, let’s wrap it up with our UI.
We have created another composable ScreenContentAccompanist( ) to build our UI. Here goes the code.
@OptIn(ExperimentalPermissionsApi::class)
@Composable
private fun ScreenContentAccompanist(
modifier: Modifier,
cameraPermission: PermissionState = cameraPermissionState(),
resultBitmap: Bitmap?,
launcherForImageCapture: ManagedActivityResultLauncher<Void?, Bitmap?>
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = modifier.padding(20.dp)
) {
Text(
text = "Create your profile here!",
style = TextStyle(
fontWeight = FontWeight.Bold,
fontSize = 20.sp
)
)
Spacer(modifier = Modifier.height(24.dp))
Divider(
modifier = Modifier.fillMaxWidth(),
color = Color.Gray
)
Spacer(modifier = Modifier.height(24.dp))
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.height(100.dp)
.width(100.dp)
.clip(shape = CircleShape)
.border(
width = 1.dp,
color = Color.Gray,
shape = CircleShape
)
) {
resultBitmap?.asImageBitmap()?.let {
Image(
bitmap = it,
contentDescription = "Captured image",
contentScale = ContentScale.Crop,
modifier = Modifier
.size(150.dp)
.clickable {
if (cameraPermission.hasPermission) {
launcherForImageCapture.launch()
} else if (!cameraPermission.hasPermission) {
cameraPermission.launchPermissionRequest()
}
}
)
}
}
Spacer(modifier = Modifier.height(16.dp))
ProfileOutlinedTextField(value = "Name")
ProfileOutlinedTextField(value = "Email")
ProfileOutlinedTextField(value = "Password")
ProfileOutlinedTextField(value = "DOB")
Spacer(modifier = Modifier.height(24.dp))
Button(
onClick = { },
modifier = Modifier
.padding(horizontal = 36.dp)
.fillMaxWidth()
) {
Text(text = "Submit")
}
}
}
Nothing crazy here, we have just added a simple code to build a registration page UI.
The ProfileOutlinedTextField( ) is nothing but a simple OutlinedTextField( ) which looks like:
@Composable
fun ProfileOutlinedTextField(
value: String,
onValueChange: (String) -> Unit = { }
) {
OutlinedTextField(
value = value,
onValueChange = onValueChange,
modifier = Modifier.padding(vertical = 10.dp),
textStyle = TextStyle(color = Color.Gray)
)
}
Calling the ScreenContentAccompanist( ) with the appropriate parameters, completes our profile screen.
@ExperimentalPermissionsApi
@Composable
fun ProfileScreenWithAccompanist() {
var resultBitmap: Bitmap? by rememberSaveable { mutableStateOf(placeHolderBitmap) }
val launcherForImageCapture = rememberLauncherForActivityResult(
contract = ActivityResultContracts.TakePicturePreview()
) {
resultBitmap = if (it.toString().isEmpty()) {
placeHolderBitmap
} else {
it
}
}
ScreenContentAccompanist(
modifier = Modifier
.fillMaxSize()
.systemBarsPadding(),
resultBitmap = resultBitmap,
launcherForImageCapture = launcherForImageCapture
)
}
Here is the working preview:
6. Use Case – User keeps denying permissions
While working with an application that requires permission, you might encounter various situations. Here are some of them discussed below:
Permission might be denied and again while using the app, thus such situations are bound to happen:
1) In older android versions, permission denied twice, needs to be changed manually in the settings.
2) In newer android versions, permission can be granted only while using the app.

Project code and resources
Link to the code repository.
- Android Jetpack Compose – Android Documentation
- Android permissions documentation
- Manifest permissions documentation
- Accompanist library documentation
- Handling permissions without any library
I would also like to extend my gratitude to Raj for contributing clean, simple, and user-friendly code to this project.
Here We Go Again : (
if (article == helpful) {
println("Like and subscribe to blog newsletter.")
} else {
println("Let me know what i should blog on.")
}
Leave a Reply