Search code examples
androidkotlinandroid-jetpack-composeandroid-permissions

How to make the argument of registerForActivityResult composable?


The following code is from the Android Developer Docs. It explains how to request permissions.

// Register the permissions callback, which handles the user's response to the
// system permissions dialog. Save the return value, an instance of
// ActivityResultLauncher. You can use either a val, as shown in this snippet,
// or a lateinit var in your onAttach() or onCreate() method.
val requestPermissionLauncher =
    registerForActivityResult(RequestPermission()
    ) { isGranted: Boolean ->
        if (isGranted) {
            // Permission is granted. Continue the action or workflow in your
            // app.
        } else {
            // Explain to the user that the feature is unavailable because the
            // features requires a permission that the user has denied. At the
            // same time, respect the user's decision. Don't link to system
            // settings in an effort to convince the user to change their
            // decision.
        }
    }

I tried to use the code in my application. When isGranted is true I tried render my main ui. But it does not work, because I get the following error:

@Composable invocations can only happen from the context of a @Composable function

I am wondering why this happens, because I call the launcher from a composable context. Is it necessary to mark every function in the call stack as @Composable? And if so, how to make the closure passed to registerForActivityResult composable?


Solution

  • You should define a state that is updated once the result is returned. That remembered state can within your statefull composable or coming as parameter. Updating the remembered state will trigger recomposition.

    Lambda passed in the registerForActivity is callback and it is passed multiple times in recomposition but only the last one that has been passed is called back.

    You can do something like this:

    @Composable
    fun OnPermissionGranted(permission : String, launch : Boolean, onGranted : @Composable () -> Unit ){
        val context = LocalContext.current
        var granted by remember { mutableStateOf(checkIfGranted(context) ) }
        val launcher = rememberLauncherForActivityResult(contract = ActivityResultContracts.RequestPermission()){
            if(it){
                granted = true
            }
        }
        LaunchedEffect(granted, launch){
           if(!granted && launch){
              launcher.launch(permission)
           }
        }
        
        if(granted){
            onGranted()
        }
    }