How to force recomposition in Jetpack Compose?

1.7k Views Asked by At

I wrote utility functions to request/check permissions in Composables (using CompositionLocal).

data class PermissionHandlerValue(
    val hasPermission: (String) -> Boolean,
    val hasPermissions: (Array<out String>) -> Array<Boolean>,
    val requestPermission: (String) -> Unit,
    val requestPermissions: (Array<out String>) -> Unit
)

val LocalPermissionHandler = compositionLocalOf<PermissionHandlerValue> { error("No implementation provided!") }

@Composable
fun ProvidePermissionHandler(content: @Composable () -> Unit) {
    CompositionLocalProvider(LocalPermissionHandler provides permissionHandlerImpl()) {
        content()
    }
}

@Composable
fun permissionHandlerImpl(): PermissionHandlerValue {
    val context = LocalContext.current
    val launcher = rememberLauncherForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) {}

    val hasPermission: (String) -> Boolean = { ContextCompat.checkSelfPermission(context, it) == PackageManager.PERMISSION_GRANTED }
    val hasPermissions: (Array<out String>) -> Array<Boolean> = { it.map { permission -> hasPermission(permission) }.toTypedArray() }

    val requestPermission: (String) -> Unit = { launcher.launch(arrayOf(it)) }
    val requestPermissions: (Array<out String>) -> Unit = { launcher.launch(it) }

    return PermissionHandlerValue(hasPermission, hasPermissions, requestPermission, requestPermissions)
}

@Composable
fun RequirePermission(permission: String, fallback: (@Composable () -> Unit)? = null, content: @Composable () -> Unit) {
    val permissionHandler = LocalPermissionHandler.current
    if (permissionHandler.hasPermission(permission))
        content()
    else if (fallback != null)
        fallback()
}

It works fine, I can request and check permissions. The problem is that its not reactive, here's an example:

setContent {
    ProvidePermissionHandler {
        val permissionHandler = LocalPermissionHandler.current

        RequirePermission(
            permission = Manifest.permission.READ_CONTACTS,
            fallback = { 
                Button(onClick = { permissionHandler.requestPermission(Manifest.permission.READ_CONTACTS) 
                }) { 
                    Text("Request permission") 
                }
            }
        ) {
            ContactsList() 
        }
    }
}

This composable(RequirePermission) will only render ContactsList if the Manifest.permission.READ_CONTACTS was granted, Otherwise the fallback component is rendered with a button that when clicked will request the permission.

After permissionHandler.requestPermission() is called and I grant the permission on the screen the fallback still shows, instead of the ContactsList (I have to re-open the app to show it).

Basically the condition in RequirePermission() is not checked again because there is no recomposition. How can I force RequirePermission() to recompose?

0

There are 0 best solutions below