Have a question on how clickable works when using ...
# compose-android
c
Have a question on how clickable works when using code that has callbacks. My use case here is trying to show the play store review on click of a button.
This is roughly my code
Copy code
if (showRatingPrompt == true) {
TextButton(
onClick = {
    viewModel.screenState.showRatingPrompt = false
    val manager = ReviewManagerFactory.create(context)

    val request = manager.requestReviewFlow()
    request.addOnCompleteListener { task ->
        if (task.isSuccessful) {
            // We got the ReviewInfo object
            val reviewInfo = task.result
            val flow = manager.launchReviewFlow(activity, reviewInfo)
            flow.addOnCompleteListener { _ ->
                // The flow has finished. The API does not indicate whether the user
                // reviewed or not, or even whether the review dialog was shown. Thus, no
                // matter the result, we continue our app flow.
            }
        } else {
            // There was some problem, log or handle the error code.
            @ReviewErrorCode
            val reviewErrorCode = (task.exception as ReviewException).errorCode
        }
    }
}) {
    Text("Text")
}
}
The review prompt code is basically copy/pasta from googles example, but I guess I'm sorta worried on what happens since the clickable launches some async code/listener.
I feel like the solution here might be to elevate this code into a ViewModel, but this is sorta a sample app I have going and am more curious of whether or not i opened pandoras box by trying to do in this in a clickable.
s
Don't they explicitly say not to do this from a button? The review dialog may simply not show up
c
Yes, kinda meant to say that from my POC part of this. Im just working with the review prompt for the first time and just trying to force it to open during testing in internal app sharing test track in google play. And my code above works... but I'm curious if there would be a case where this wouldn't work because this block of code would technically leave composition since I'm doing if(showRatingPrompt) and then right away set that to false
hence when i said "i think i opened pandoras box". Now i basically have this question that I don't know the answer to, hence i figured id ask. lol
f
you should use a
LaunchedEffect
keyed off the
showRatingPromp
flag, and do your launching of the review flow there
this is how I've done it
Copy code
LaunchedEffect(key1 = state.showReviewDialog) {
        if (state.showReviewDialog) {
            context.locateActivity()?.let { activity ->
                launchInAppReview(activity) {
                    eventSink(CalculatorScreen.Event.ReviewDialogShown(it))
                }
            } ?: eventSink(CalculatorScreen.Event.ReviewDialogShown(false))
        }
    }
with
Copy code
private fun launchInAppReview(activity: Activity, completeListener: (Boolean) -> Unit) {
    val reviewManager = ReviewManagerFactory.create(activity)
    val requestReviewFlow = reviewManager.requestReviewFlow()
    requestReviewFlow.addOnCompleteListener { request ->
        if (request.isSuccessful) {
            val reviewInfo = request.result
            reviewManager.launchReviewFlow(activity, reviewInfo)
            completeListener(true)
        } else {
            completeListener(false)
        }
    }
}
c
Thanks for the response. In my example though, is anything "wrong"? like is it bad to launch a callback based thingy in a button click listener?
f
looks fine to me, but this part I'd change
Copy code
viewModel.screenState.showRatingPrompt = false
the viewmodel should own its state, here the UI is updating the viewmodel's state
c
Makes sense to me. Appreciate the pair of eyes @Francesc!