update app_review

This commit is contained in:
2026-01-15 00:37:49 -08:00
parent 2e5f0f1ae1
commit a53ed330c5
70 changed files with 2227 additions and 2130 deletions
+175 -197
View File
@@ -1,206 +1,37 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:http/http.dart' as http;
import 'package:package_info_plus/package_info_plus.dart';
import 'package:url_launcher/url_launcher.dart';
import 'src/exceptions.dart';
import 'src/messages.g.dart';
class AppReview {
static const Duration kDefaultDuration = Duration(minutes: 5);
static const MethodChannel _channel = MethodChannel('app_review');
static AppReviewApi _api = AppReviewApi();
//----------------------------------------------------------------------------
// Maintain original interface
// Public Interface
//----------------------------------------------------------------------------
/// Returns package name for application.
static Future<String?> get getAppID => getBundleName();
static Future<String?> getAppId() => getBundleName();
/// Returns package name for application.
static Future<String?> getBundleName() async {
if (_appBundle != null) {
return _appBundle;
}
try {
_appBundle = await _api.getBundleId();
return _appBundle;
} catch (e) {
return null;
}
}
/// Returns Apple ID for iOS application.
///
/// If there is no such application in App Store - returns empty string.
static Future<String?> get getiOSAppID => getIosAppId();
/// Request review.
///
/// Tells StoreKit / Play Store to ask the user to rate or review your app, if appropriate.
/// Supported only in iOS 10.3+ and Android with Play Services installed (see [isRequestReviewAvailable]).
///
/// Returns string with details message.
static Future<String?> get requestReview async {
if (Platform.isIOS) {
return openIosReview();
}
if (Platform.isAndroid) {
return openAndroidReview();
}
return null;
}
/// Request review.
///
/// Tells StoreKit to ask the user to rate or review your app, if appropriate.
/// Supported only in iOS 10.3+ and Android with Play Services installed (see [isRequestReviewAvailable]).
///
/// Returns string with details message.
static Future<Timer> requestReviewDelayed([Duration? duration]) async =>
Timer(duration ?? kDefaultDuration, () => requestReview);
/// Check if [requestReview] feature available.
static Future<bool> get isRequestReviewAvailable async {
if (Platform.isIOS || Platform.isAndroid) {
try {
final result =
await _channel.invokeMethod<String>('isRequestReviewAvailable');
return result == '1';
} finally {}
}
return false;
}
/// Open store page with action write review.
///
/// Supported only for iOS, on Android [storeListing] will be executed.
static Future<String?> get writeReview async {
if (Platform.isIOS) {
return openIosReview(compose: true);
}
if (Platform.isAndroid) {
return openAndroidReview();
}
return null;
}
/// Navigates to Store Listing in Google Play/App Store.
///
/// Returns string with details message.
static Future<String?> get storeListing async {
if (Platform.isIOS) {
return openAppStore();
}
if (Platform.isAndroid) {
return openGooglePlay();
}
return null;
}
//----------------------------------------------------------------------------
// Helper methods (added by @shinsenter)
//----------------------------------------------------------------------------
static PackageInfo? _packageInfo;
static String? _appCountry;
static String? _appBundle;
static String? _appId;
/// It would be great if I could add country code into AppStore lookup URL.
/// Eg: AppReview.setCountryCode('jp');
static void setCountryCode(String code) =>
_appCountry = code.isEmpty ? null : code;
/// Require app review for iOS
static Future<String?> openIosReview({
String? appId,
bool compose = false,
}) async {
if (compose) {
final id = appId ?? (await getIosAppId()) ?? '';
// New format: https://apps.apple.com/app/idYOURAPPSTOREID?action=write-review
final reviewUrl = 'apps.apple.com/app/id$id';
final uri = Uri.parse('https://$reviewUrl?action=write-review');
if (await canLaunchUrl(uri)) {
debugPrint('launching store page');
await launchUrl(uri, mode: LaunchMode.externalApplication);
return 'Launched App Store Directly: $reviewUrl';
}
await launchUrl(uri, mode: LaunchMode.externalApplication);
return 'Launched App Store: $reviewUrl';
}
try {
return _channel.invokeMethod<String>('requestReview');
} finally {}
}
/// Require app review for Android
static Future<String?> openAndroidReview() {
try {
return _channel.invokeMethod<String>('requestReview');
} on Error {
return openGooglePlay();
}
}
/// Open in AppStore
static Future<String> openAppStore({String? fallbackUrl}) async {
final appId = await getIosAppId() ?? '';
if (appId.isNotEmpty) {
launchUrl(Uri.parse('https://apps.apple.com/app/id$appId'),
mode: LaunchMode.externalApplication);
return 'Launched App Store';
}
if (fallbackUrl != null) {
launchUrl(Uri.parse(fallbackUrl), mode: LaunchMode.externalApplication);
return 'Launched App Store via $fallbackUrl';
}
return 'Not found in App Store';
}
/// Open in GooglePlay
static Future<String> openGooglePlay({String? fallbackUrl}) async {
final bundle = await getBundleName() ?? '';
final markerUrl = 'market://details?id=$bundle';
final uri = Uri.parse(markerUrl);
if (await canLaunchUrl(uri)) {
debugPrint('launching store page');
launchUrl(uri, mode: LaunchMode.externalApplication);
return 'Launched Google Play Directly: $bundle';
}
if (fallbackUrl != null) {
launchUrl(Uri.parse(fallbackUrl), mode: LaunchMode.externalApplication);
return 'Launched Google Play via $fallbackUrl';
}
launchUrl(
Uri.parse('https://play.google.com/store/apps/details?id=$bundle'));
return 'Launched Google Play: $bundle';
}
/// Lazy load package info instance
static Future<PackageInfo?> getPackageInfo() async {
_packageInfo ??= await PackageInfo.fromPlatform();
debugPrint('App Name: ${_packageInfo!.appName}\n'
'Package Name: ${_packageInfo!.packageName}\n'
'Version: ${_packageInfo!.version}\n'
'Build Number: ${_packageInfo!.buildNumber}');
return _packageInfo;
}
/// Get app bundle name
static Future<String?> getBundleName() async {
_appBundle ??= (await getPackageInfo())?.packageName ?? '';
return _appBundle;
}
/// Get app's AppStore ID (public app only)
static Future<String?> getIosAppId({
String? countryCode,
String? bundleId,
@@ -223,15 +54,7 @@ class AppReview {
if (id.isNotEmpty) {
try {
final result = await http
.get(Uri.parse(
'https://itunes.apple.com/$country/lookup?bundleId=$id'))
.timeout(const Duration(seconds: 5));
final Map json = jsonDecode(result.body);
final List results = json['results'] as List;
if (results.isNotEmpty) {
appId = results[0]['trackId']?.toString();
}
appId = await _api.lookupAppId(id, country);
} catch (e) {
debugPrint('Error fetching app ID: $e');
} finally {
@@ -245,4 +68,159 @@ class AppReview {
return appId ?? '';
}
/// Request review.
///
/// Tells StoreKit / Play Store to ask the user to rate or review your app, if appropriate.
/// Supported only in iOS 10.3+ and Android with Play Services installed (see [isRequestReviewAvailable]).
///
/// Throws [AppReviewException] if request fails.
static Future<void> requestReview({
bool useAndroidTestMode = false,
}) async {
final result = await _api.requestReview(useAndroidTestMode);
if (result != null && result.toLowerCase().contains("not available")) {
throw AppReviewUnavailableException(result);
}
}
/// Check if [requestReview] feature available.
static Future<bool> isRequestReviewAvailable() async {
if (Platform.isIOS || Platform.isAndroid || Platform.isMacOS) {
try {
return await _api.isRequestReviewAvailable();
} catch (e) {
return false;
}
}
return false;
}
/// Open store page with action write review.
///
/// Supported only for iOS, on Android [storeListing] will be executed.
///
/// [appStoreId] - App ID for iOS App Store (e.g. 1234567890)
/// [playStoreId] - Package name for Google Play Store (e.g. com.example.app) on Android.
static Future<void> writeReview({
String? appStoreId,
String? playStoreId,
bool useAndroidTestMode = false,
}) async {
if (Platform.isIOS || Platform.isMacOS) {
await _openIosReview(
appId: appStoreId,
compose: true,
);
return;
}
if (Platform.isAndroid) {
await _openAndroidReview(
appId: playStoreId,
useAndroidTestMode: useAndroidTestMode,
);
return;
}
}
/// Navigates to Store Listing in Google Play/App Store.
///
/// [appStoreId] - App ID for iOS App Store (e.g. 1234567890)
/// [playStoreId] - Package name for Google Play Store (e.g. com.example.app) on Android.
static Future<void> storeListing({
String? appStoreId,
String? playStoreId,
}) async {
if (Platform.isIOS || Platform.isMacOS) {
await openAppStore(appId: appStoreId);
return;
}
if (Platform.isAndroid) {
await openGooglePlay(appId: playStoreId);
return;
}
}
//----------------------------------------------------------------------------
// Helper methods
//----------------------------------------------------------------------------
static String? _appCountry;
static String? _appBundle;
static String? _appId;
/// It would be great if I could add country code into AppStore lookup URL.
/// Eg: AppReview.setCountryCode('jp');
static void setCountryCode(String code) =>
_appCountry = code.isEmpty ? null : code;
/// Require app review for iOS
static Future<void> _openIosReview({
String? appId,
bool compose = false,
}) async {
if (compose) {
final id = appId ?? (await getIosAppId()) ?? '';
try {
await _api.openAppStoreReview(id);
} catch (e) {
throw AppReviewStoreListingFailedException(e.toString());
}
return;
}
try {
final result = await _api.requestReview(false);
if (result != null && result.toLowerCase().contains("not available")) {
throw AppReviewUnavailableException(result);
}
} catch (e) {
if (e is AppReviewException) rethrow;
throw AppReviewRequestFailedException(e.toString());
}
}
/// Require app review for Android
static Future<void> _openAndroidReview(
{String? appId, bool useAndroidTestMode = false}) async {
try {
final result = await _api.requestReview(useAndroidTestMode);
if (result != null && result.toLowerCase().contains("not available")) {
throw AppReviewUnavailableException(result);
}
} catch (e) {
if (e is AppReviewException) rethrow;
// If request fails, try opening store
await openGooglePlay(appId: appId);
}
}
/// Open in AppStore
static Future<void> openAppStore({
String? fallbackUrl,
String? appId,
}) async {
final id = appId ?? await getIosAppId() ?? '';
try {
await _api.openStoreListing(id);
} catch (e) {
throw AppReviewStoreListingFailedException(e.toString());
}
}
/// Open in GooglePlay
static Future<void> openGooglePlay({
String? fallbackUrl,
String? appId,
}) async {
final bundle = appId ?? await getBundleName() ?? '';
try {
await _api.openStoreListing(bundle);
} catch (e) {
throw AppReviewStoreListingFailedException(e.toString());
}
}
}