DEV Community

Cover image for Google Play In-App Review API: why the card won't show
ExtensionBooster
ExtensionBooster

Posted on Originally published at appranker.net

Google Play In-App Review API: why the card won't show

Originally published on AppRanker.

You wired up the Google Play In-App Review API, the task completes without an error, and no rating card ever appears. Usually nothing is broken: the card is quota-limited, the API never tells you what happened, and only one of Google's three test methods shows the real flow. This post gives you a working setup for Kotlin, Expo and Flutter, a rule for when to call it, and a table of the usual reasons the card stays hidden.

What the API does (and won't tell you)

The flow is two calls. requestReviewFlow() fetches a ReviewInfo object from Google Play, and launchReviewFlow(activity, reviewInfo) shows a native Play card over your app where the user picks 1 to 5 stars and can add a comment. It runs on Android 5.0 (API 21) and later, including tablets and Google TV, plus ChromeOS, but only where the Google Play Store is installed.

Three facts shape everything else:

  • You get no result. The completion callback fires whether the user rated, dismissed the card, or never saw it. Your code must carry on the same way in every case.
  • There is a quota. Google limits how often one user sees the card. Its own example is that calling it more than once in "less than a month" may display nothing, and the exact limit can change without notice.
  • The rules are strict. No "Do you like the app?" question before or during the card, no reshaping or overlaying it, and no rewards for ratings. Play's ratings policy bans incentivized feedback outright.

The quota is why Google says not to put the flow behind a button. A user who has used up their quota taps it and sees nothing.

Decide when to ask before writing the call

Since each user gets roughly one shot per period, your trigger logic matters more than the integration. Aim for a moment right after the user succeeded at something, while they are between tasks, once they have used the app enough to judge it.

App type Ask here Never here
Productivity After the 3rd completed export or project First launch, mid-typing
Fitness Workout summary screen During a workout, after a missed goal
Game After a level win, before the next level Mid-game, after a loss
Utility After the tool has worked several times After an error or crash recovery

A small eligibility gate keeps calls rare. The thresholds below are an example to tune, not a Google requirement:

private const val DAY_MS = 86_400_000L

fun reviewEligible(prefs: SharedPreferences, now: Long = System.currentTimeMillis()): Boolean =
    prefs.getInt("core_action_count", 0) >= 3 &&
    now - prefs.getLong("first_open_at", now) >= 3 * DAY_MS &&
    now - prefs.getLong("review_last_asked_at", 0L) >= 90 * DAY_MS
Enter fullscreen mode Exit fullscreen mode

Save review_last_asked_at at the moment you launch the flow. You will never learn whether a rating was left, so launch time is the only timestamp you can trust.

The integration in Kotlin, Expo and Flutter

Kotlin. Add com.google.android.play:review:2.0.2 (and review-ktx with the same version for Kotlin extensions). Fetch ReviewInfo shortly before the success moment, since it expires after a while:

class RatingAsk(private val activity: Activity) {
    private val manager = ReviewManagerFactory.create(activity)
    private var info: ReviewInfo? = null

    fun warmUp() = manager.requestReviewFlow().addOnCompleteListener { t ->
        info = if (t.isSuccessful) t.result else null
        if (!t.isSuccessful) Log.w("RatingAsk", "code=${(t.exception as? ReviewException)?.errorCode}")
    }

    fun show(next: () -> Unit) {
        val ready = info ?: return next()
        manager.launchReviewFlow(activity, ready).addOnCompleteListener {
            info = null
            next() // runs no matter what the user did
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Errors you may log: PLAY_STORE_NOT_FOUND (-1), INVALID_REQUEST (-2) and INTERNAL_ERROR (-100). Log them, never show them to the user, and wait for the flow to finish before moving to the next screen.

Expo. npx expo install expo-store-review, then check StoreReview.hasAction() before calling StoreReview.requestReview(). Bare React Native apps can use react-native-in-app-review.

Flutter. The in_app_review package gives you both sides:

final review = InAppReview.instance;

Future<void> askIfEligible(bool eligible) async {
  if (eligible && await review.isAvailable()) await review.requestReview();
}

// Settings "Rate us" item: not quota-limited
Future<void> rateUs() => review.openStoreListing();
Enter fullscreen mode Exit fullscreen mode

For a permanent "Rate us" menu entry on Android, link to https://play.google.com/store/apps/details?id=<your.app.id>&showAllReviews=true (or the market:// form) instead of calling the API.

Testing: pick the right method

Most "it's broken" reports come from testing in a setup that cannot show a working card.

Method Use it for Catch
Internal test track Real end-to-end card None; quota is not applied here
Internal app sharing Quick build checks Submit button is disabled
FakeReviewManager Unit and integration tests No UI, always succeeds

For the internal test track, all four must hold: the account is on the track, it is the primary account selected in the Play Store, it installed the app from Play, and it has not reviewed the app yet. After that first Play install, you can push new builds from Android Studio and keep testing.

Card not showing: quick diagnosis

What you see Likely cause Fix
Completes, no card, debug build This account never installed from Play Install once from the internal track
Worked once, never again Quota hit or already reviewed Delete your review, or use the internal track
Submit greyed out Internal app sharing install Expected; use the internal track
Never shows on work phone Enterprise-protected account Test with a personal Gmail account
PLAY_STORE_NOT_FOUND Emulator without Play Use an image with Google Play
Works on one account only Wrong primary account in Play Store Switch accounts in the Play Store

If none fit, check that your eligibility gate ever returns true. A gate that never opens looks exactly like a broken API.

Key takeaways

  • The API reports nothing back, so always continue your flow on completion.
  • A quota applies (roughly monthly in Google's example), so never trigger it from a button.
  • No pre-questions, no rewards, no altering the card.
  • Ask right after a success, gated by usage count, days since install and time since the last ask.
  • Test on the internal test track with a personal account that has not reviewed the app.

The original covers the policy quotes, the Bare React Native setup and an FAQ in more depth: see the complete Google Play In-App Review API guide for Android, React Native and Flutter. For more on getting your app seen once the ratings come in, browse the AppRanker app leaderboard and ASO guides.

What trigger moment has worked best for your app's review prompt, and did you ever catch it silently burning the quota too early?

Top comments (0)