◉ANTIGRAVITY LABJP
Articles/App Development
▣ App Development/2026-06-23Advanced

The Review Prompt Fired but Nothing Appeared — Designing Around Play In-App Review's Quota and No-Show Guarantee

Play In-App Review's launchReviewFlow can succeed without ever showing a dialog. This walks through the three traps — quota, no display guarantee, and silent testing — and the engagement-based trigger design that fires at the right moment without colliding with ads, with steps to have Antigravity implement it.

in-app-reviewAndroid27monetization31Kotlin4Antigravity377

✦ Premium Article

I still remember the day I wired Play's In-App Review into one of my calming wallpaper apps. I called launchReviewFlow, the addOnCompleteListener came back successful, the log printed "review flow finished" — and yet the star-rating dialog never appeared on screen. Not once.

I swapped test devices, swapped accounts, and tried dozens of times with the same result. The code looked like it was working, but the one thing I cared about stayed silent. As an indie developer who has been shipping apps for years, even I spent the better part of a day convinced my own implementation was broken, hunting for the bug.

The short version: what was broken wasn't the implementation, it was my assumption. Play In-App Review is not a "call it and it shows" API. Google decides whether to display it, and that decision is invisible to you. This article is the trigger design that only becomes correct once you put "no display guarantee" at the center, written up alongside the implementation code.

The contract: a successful callback is not a display guarantee

The first mental shift is that completion of launchReviewFlow does not mean "the dialog was shown." By design, there is no API that tells your app whether the review flow was actually displayed. The completion listener reports only that the flow finished — it cannot distinguish between shown, suppressed by quota, or already reviewed.

// ❌ The common misread — assuming "completed" means "shown"
manager.launchReviewFlow(activity, reviewInfo)
    .addOnCompleteListener { task ->
        // Even with task.isSuccessful == true, the dialog may never have appeared
        // Do NOT set a "reviewed" flag here
        markReviewedAndNeverAskAgain()   // ← this is what breeds the silence
    }

Half of the day I burned came from exactly this. Because I was persisting a "don't ask again" flag in the completion callback, a single run of the flow put the app into a state where it would never fire again — a trap I had built myself. The correct move is to do nothing meaningful in the callback (or, at most, advance an internal cooldown). The starting point is to stop trying to determine, from your side, whether a review happened.

What you want to knowDoes the API return it?Design to adopt
Was the dialog shown?NoDon't make UX depend on display
Did the user leave a rating?NoNo post-rating rewards or branching
Was it suppressed by quota?No (returns success)Cooldown, ready to re-fire later
Did the flow finish?YesUse only to resume navigation

Quota: the invisible wall

The next wall is quota. In-App Review caps how often the review prompt can be offered to the same user. Call launchReviewFlow repeatedly in a short window and the excess calls are quietly ignored — no dialog. And as above, that suppression is invisible from the callback. It comes back as success.

This is genuinely nasty for an indie developer. During testing you launch the app over and over and keep satisfying your review conditions. The result: your own development account hits the quota first, producing exactly the initial symptom — "I implemented it and it won't show."

The design implication is clear: throttle firing deliberately on your side rather than leaning on the API's limit. In my calming apps, the review prompt only fires when "enough time has passed across sessions" and "a satisfaction signal is present." The API quota is just the last safety net; ideally your own trigger policy has already narrowed things down well before it.

✦

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
✦Understand the real contract — a successful launchReviewFlow does not mean a dialog was shown — and build code that never makes UX depend on that callback
✦Ship a review prompt that actually works in production by avoiding the three traps: quota, ReviewInfo lifetime, and silent no-ops in test builds
✦Port an engagement-based trigger that fires only at the peak of satisfaction and yields to ads and paywalls instead of stacking requests
Secure payment via Stripe · Cancel anytime
✦

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Antigravity Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $15 for lifetime access
View Membership →

Related Articles

▣ App Dev2026-05-25
One Month Splitting Antigravity's Inline Edit and Agent Mode Across Four Wallpaper Apps
A month of notes from running Antigravity's Inline Edit and Agent Mode across four production wallpaper apps — with real counts, the decision rule I wrote into AGENTS.md, a one-pass dSYM fix, and how credit cost factors in.
▣ App Dev2026-09-29
Purging Cloudflare Didn't Bring Back the New Wallpapers: the Stale JPEGs Were Held by XPageSpeed on the Origin
An operations record from a wallpaper app: images overwritten at the same URL kept arriving stale even after a Cloudflare purge. Covers the three-way local/origin/edge probe that names the guilty layer, when prefix purge beats per-URL purge, and how to switch off Xserver's XPageSpeed in .htaccess without a 500.
▣ App Dev2026-09-19
I Asked for the Whole Quote PDF Tool at Once, Then Split It Into Four Requests
How I moved my client quote paperwork from copy-and-edit to a small PDF tool built with Antigravity, why the first single-shot request had to be thrown away, and the four smaller requests that replaced it, with working code.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links