How to Ask for In-App Reviews in Your Flutter App

App reviews play an important role in the success of your apps. They not only provide essential feedback but also significantly affect the app's store performance:

  • Positive reviews boost the app's ranking and drive more downloads.
  • Negative reviews can discourage new users from downloading.

For example, here are the reviews from my Flutter Tips app:

App Store Reviews for the Flutter Tips app

Glowing reviews don't just fall from the sky: you have to earn them by making a good app and make it as easy as possible for users to leave a review.

The best way to do this is by showing an in-app rating prompt at the right moment—when users are most engaged and satisfied. This could be after completing a task, leveling up in a game, or using a feature successfully.

Timing the prompt correctly increases the chances of getting positive reviews, boosting your app’s rating and visibility in the app store.

And by using the in_app_review package and a bit of extra code, you can do exactly that.

The in_app_review package

The in_app_review package allows you to ask for reviews in two different ways:

  1. Call-to-action: by redirecting the user to the store with the openStoreListing API
  2. Programmatically: by triggering the in-app review prompt with the requestReview API
The in-app review prompt on iOS

The second method is most effective, since it can be triggered when the user is most satisfied with the app. For example, I've programmed my Flutter Tips app to show the prompt after users like 5 tips in the app:

When the user likes N tips, show the in-app rating prompt

What we will cover

In this article, I'll show you how to apply the same technique to your apps.

Here's what we will cover:

  • in_app_review installation and how to show the app-review prompt programmatically
  • How to avoid showing the prompt too early (by following the quotas on the app stores)
  • How to use analytics to ensure the prompt shows at the right time

When to show the prompt?

Showing the prompt is the easy part.

The real challenge is deciding when to do it:

  • too early, and you will annoy your users (so many apps get this wrong!)
  • too late, and hardly any users will even see it at all

As we will see, by using analytics and a data-driven approach, we can show the prompt at the right time, for the most engaged users, thus maximising our chances of getting positive reviews.

Ready? Let's go! 🚀

This article is not a step-by-step tutorial—it's more of a high-level guide showing how things fit together. If you want to go deeper, check my latest course about Flutter in Production.

Installation

Installing the in_app_review package is easy enough:

dart pub add in_app_review:2.0.9 flutter pub get

If you're using Riverpod, consider creating a separate provider for this:

// in_app_review_provider.dart import 'package:in_app_review/in_app_review.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'in_app_review_provider.g.dart'; @riverpod InAppReview inAppReview(InAppReviewRef ref) { return InAppReview.instance; }

Since the code above relies on code generation, you'll need to run dart run build_runner watch -d to generate your provider. To learn more, read: How to Auto-Generate your Providers with Flutter Riverpod Generator.

Showing the in-app review prompt

As discussed, we need to show the prompt at the right time.

This means that you need to choose the single, most important event that happens when the user is most satisfied with your app (e.g. completed a task, uses a feature successfully).

For my Flutter Tips app, this is the "tip liked" event:

Choosing an event that will trigger the in-app review prompt

The callback handler for this button looks something like this:

Future<void> updateTipLiked(int tipIndex, bool isLiked) async { if (isLiked) { // * Show app review prompt based on some conditional logic await inAppRatingService.requestReviewIfNeeded( userTotalLikesCount: userTotalLikesCount ); // * Log the event with analytics unawaited(analyticsFacade.trackTipLiked( tipIndex: tipIndex, userTotalLikesCount: userTotalLikesCount, )); } }

Two things to note:

  • The updateTipLiked method has two purposes: show the app review prompt and track the event.
  • Both the requestReviewIfNeeded and trackTipLiked methods take userTotalLikesCount as an argument.

We'll get back to some of these details later. But for now, let's focus on the InAppRatingService.

If you're not familiar with the unawaited function, read: Futures: await vs unawaited vs ignore and Use unawaited for your analytics calls.

The InAppRatingService class

To handle all the in-app review logic, we can use a dedicated class that looks like this:

/// Helper class used to show the in-app rating prompt when a certain number of /// tips has been liked class InAppRatingService { const InAppRatingService(this.ref); final Ref ref; // * Used to show the prompt InAppReview get _inAppReview => ref.read(inAppReviewProvider); /// Requests a review if certain conditions are met Future<void> requestReviewIfNeeded({required int userTotalLikesCount}) async { // * Don't show rating prompt on web (not supported) if (kIsWeb) { return; } // TODO: Only show prompt after a certain number of tips has been liked // * If we can show a review dialog if (await _inAppReview.isAvailable()) { // * Request the review await _inAppReview.requestReview(); } } } @riverpod InAppRatingService inAppRatingService(InAppRatingServiceRef ref) { return InAppRatingService(ref); }

Some notes:

  • The class takes a ref argument, which can be used to read other providers (such as inAppReviewProvider).
  • The requestReviewIfNeeded method doesn't do anything if kIsWeb is true (we can only show the prompt on iOS and Android).
  • We check if the review dialog is available before showing it.

If your app doesn't use Riverpod, you can delete the provider and inject InAppReview as a constructor argument instead.

Does this code work as intended?

If we added the InAppRatingService class above to Flutter Tips app and ran it on iOS, the rating prompt would appear as soon as we like a tip for the first time, and then again for each subsequent like:

Showing the rating prompt too early will annoy the user

That's a bit too eager!

To avoid giving a bad first impression, we should add some logic that says "wait until the user liked N tips before showing the prompt".

Showing the Review Prompt After N Events

To accomplish what we want, we can use the userTotalLikesCount variable I mentioned before:

Future<void> updateTipLiked(int tipIndex, bool isLiked) async { if (isLiked) { // * Show app review prompt based on some conditional logic await inAppRatingService.requestReviewIfNeeded( userTotalLikesCount: userTotalLikesCount ); // * Log the event with analytics unawaited(analyticsFacade.trackTipLiked( tipIndex: tipIndex, userTotalLikesCount: userTotalLikesCount, )); } }

In your apps, this variable may have a different name. You could store it locally with Shared Preferences, and increment it every time the user completes a certain action.

Here's an updated version of the InAppReviewService:

/// Helper class used to show the in-app rating prompt when a certain number of /// tips has been liked class InAppRatingService { const InAppRatingService(this.ref); final Ref ref; // * Used to show the prompt InAppReview get _inAppReview => ref.read(inAppReviewProvider); // * Used to keep track of how many times we've requested // * a review from the user SharedPreferences get _sharedPreferences => ref.read(sharedPreferencesProvider).requireValue; static const key = 'in_app_rating_prompt_count'; int get _inAppReviewRequestCount => _sharedPreferences.getInt(key) ?? 0; /// Requests a review if certain conditions are met Future<void> requestReviewIfNeeded({required int userTotalLikesCount}) async { // * Don't show rating prompt on web (not supported) if (kIsWeb) { return; } // * If we can show a review dialog if (await _inAppReview.isAvailable()) { // * Use an exponential backoff function: // * - 1st request after 5 liked tips // * - 2nd request after another 10 liked tips // * - 3rd request after another 20 liked tips if (completedTasksCount >= 5 && _inAppReviewRequestCount == 0 || completedTasksCount >= 15 && _inAppReviewRequestCount == 1 || completedTasksCount >= 35 && _inAppReviewRequestCount == 2) { // * Request the review await _inAppReview.requestReview(); // * Increment the count await _sharedPreferences.setInt(key, _inAppReviewRequestCount + 1); } } } }

Note about storing the review request count with Shared Preferences

The code above uses Shared Preferences to keep track of how many times we've requested an in-app review.

This ensures that the request count is persisted locally and can be retrieved even if we quit the app and restart it. However, if we delete and reinstall the app, or clear its storage, the count will be reset, but the app stores will still remember the quota (more on this below).

If you want to persist this kind of information across app reinstalls and your app supports authentication, consider storing the event count on your remote database, for each user.


Reviewing the Conditional Logic

The most important code in the InAppReviewService class is this:

// * Use an exponential backoff function: // * - 1st request after 5 liked tips // * - 2nd request after another 10 liked tips // * - 3rd request after another 20 liked tips if (completedTasksCount >= 5 && _inAppReviewRequestCount == 0 || completedTasksCount >= 15 && _inAppReviewRequestCount == 1 || completedTasksCount >= 35 && _inAppReviewRequestCount == 2) { // * Request the review await _inAppReview.requestReview(); // * Increment the count await _sharedPreferences.setInt(key, _inAppReviewRequestCount + 1); }

By adding the conditional logic above, we can ensure the review is requested after 5, 15, and 35 liked tips, respectively.

This is in line with the iOS App Store quotas, which state that:

The system automatically limits the display of the prompt to three occurrences per app within a 365-day period (source).

But hold on! 5, 15, and 35 are arbitrary numbers! How have I decided they were optimal for my Flutter Tips app?

The answer lies in my analytics. 👇

Making Data-Driven Decisions with Analytics

Recall that in addition to requesting the in-app review, I'm also calling this method when a user likes a tip:

// * Log the event with analytics unawaited(analyticsFacade.trackTipLiked( tipIndex: tipIndex, userTotalLikesCount: userTotalLikesCount, ));

Under the hood, this sends a custom event named "Tip Liked" to Mixpanel.

As a result, after publishing my app, I created a custom analytics report that looks like this:

75th and 90th percentiles for the user total likes in the Flutter Tips App
75th and 90th percentiles for the user total likes in the Flutter Tips App

This shows the maximum number of user total likes on the 75th and 90th percentile across all users.

Based on the given time period, I can see that, on average:

  • users on the 75th percentile like 6.9 tips
  • users on the 90th percentile like 17.1 tips

In plain English, this tells me how many tips are liked by the most engaged users. As a result, 5, 15, and 35 seemed to be reasonable thresholds for my app:

if (userTotalLikesCount >= 5 && inAppRatingPromptCount == 0 || userTotalLikesCount >= 15 && inAppRatingPromptCount == 1 || userTotalLikesCount >= 35 && inAppRatingPromptCount == 2) { // Request in-app review }

But how should you approach this in your apps?

Data-Driven In-App Review Prompt

Here are some guidelines for showing the app review prompt and maximising the number of positive reviews in your apps:

  1. Decide which is the most important event that happens when the user is most satisfied with your app (e.g. completed a task, uses a feature successfully)
  2. Add some analytics code to track that event, as well as how many times it has happened for that user (or device)
  3. Launch the app on the stores
  4. Create a custom report and measure the 75th and 90th percentile for that event (this is easy to do with Mixpanel)
  5. Once you have enough data, add the in-app review logic and choose the appropriate thresholds, as shown above
  6. Release a new version of your app

This approach has served me well, and I plan to use it in all my apps. Feel free to borrow the code in this article and tweak it for your own needs.

Caveat: if users don't enjoy using your app, you're more likely to get negative reviews. So make sure you build a good app first, and then you can optimise for getting more reviews. I recommend collecting user feedback separately with the feedback package, which also offers a Sentry plugin.

Conclusion

The in_app_review package makes it super easy to show an in-app review prompt in your app.

But the real challenge is deciding when to show the prompt:

  • too early, and you will annoy your users
  • too late, and hardly any users will even see it at all

As we've seen, by using analytics and a data-driven approach, we can ensure the prompt is shown at the right time for the most engaged users, thus maximising our chances of getting positive reviews.

And with this, I wish you the best in launching your apps! ⭐️

New course: Flutter in Production

In-app reviews play an important role in the success of your apps in the stores.

But when it comes to shipping and monitoring apps in production, there are many more things to consider:

  • Preparing for release: splash screens, flavors, environments, error reporting, analytics, force update, privacy, T&Cs
  • App Submissions: app store metadata & screenshots, compliance, testing vs distribution tracks, dealing with rejections
  • Release automation: CI workflows, environment variables, custom build steps, code signing, uploading to the stores
  • Post-release: error monitoring, bug fixes, addressing user feedback, adding new features, over-the-air updates

My latest course will help you get your app to the stores faster and with fewer headaches.

If you’re interested, you can learn more and join the waitlist here. 👇

Want More?

Invest in yourself with my high-quality Flutter courses.

Flutter Foundations Course

Flutter Foundations Course

Learn about State Management, App Architecture, Navigation, Testing, and much more by building a Flutter eCommerce app on iOS, Android, and web.

Flutter & Firebase Masterclass

Flutter & Firebase Masterclass

Learn about Firebase Auth, Cloud Firestore, Cloud Functions, Stripe payments, and much more by building a full-stack eCommerce app with Flutter & Firebase.

The Complete Dart Developer Guide

The Complete Dart Developer Guide

Learn Dart Programming in depth. Includes: basic to advanced topics, exercises, and projects. Last updated to Dart 2.15.

Flutter Animations Masterclass

Flutter Animations Masterclass

Master Flutter animations and build a completely custom habit tracking application.