Skip to main content

flutter_in_store_app_version_checker 4.0.0 copy "flutter_in_store_app_version_checker: ^4.0.0" to clipboard
flutter_in_store_app_version_checker: ^4.0.0 copied to clipboard

A lightweight Flutter plugin to check app versions in Google Play, Apple App Store, ApkPure, RuStore, and AppGallery

flutter_in_store_app_version_checker #

Pub Version Pub Points Pub Likes Downloads codecov GitHub stars

Description #

Find out whether a newer version of your app is published on Google Play, RuStore, AppGallery, ApkPure or the Apple App Store โ€” with one call and no backend.

final res = await InStoreAppVersionChecker.instance.checkUpdate(
  const InStoreAppVersionCheckerParams(locale: 'en-US'),
);
if (res.canUpdate) showUpdateDialog(res.newVersion, res.appURL);

Features #

  • ๐Ÿ” One call โ€” returns the installed version, the store version, the store URL and whether an update is available.
  • ๐Ÿช Google Play, RuStore, AppGallery, ApkPure and App Store out of the box.
  • ๐ŸŒ Regional storefronts โ€” check the App Store / Google Play for a specific country and language.
  • ๐Ÿงฎ Semver-aware comparison โ€” pre-releases, build metadata and 1.2 vs 1.2.0 are handled correctly.
  • ๐Ÿชถ Lightweight โ€” depends only on http and meta; installed app metadata is read by the plugin's own native code, no package_info_plus required.
  • ๐Ÿงช Testable โ€” inject your own http.Client; well covered by unit tests.

Getting started #

Install #

flutter pub add flutter_in_store_app_version_checker

Requirements: Flutter >=3.44.0, Dart >=3.12.0 <4.0.0.

Supported platforms #

Platform Stores
Android Google Play, ApkPure, RuStore, AppGallery
iOS Apple App Store

Other platforms (Web, Windows, Linux, macOS) are not supported.

Examples #

Check for an update #

Package name and current version are read from the installed app automatically.

import 'package:flutter_in_store_app_version_checker/flutter_in_store_app_version_checker.dart';

Future<void> checkForUpdate() async {
  final res = await InStoreAppVersionChecker.instance.checkUpdate(
    const InStoreAppVersionCheckerParams(locale: 'en-US'),
  );

  if (res.isError) {
    debugPrint('Update check failed: ${res.errorMessage}');
    return;
  }

  debugPrint('Current version: ${res.currentVersion}');
  debugPrint('Store version  : ${res.newVersion}');
  debugPrint('Store URL      : ${res.appURL}');
  debugPrint('Can update     : ${res.canUpdate}');
}

Show an update dialog #

A typical "new version available" prompt, using url_launcher to open the store page:

Future<void> showUpdateDialogIfNeeded(BuildContext context) async {
  final res = await InStoreAppVersionChecker.instance.checkUpdate(
    const InStoreAppVersionCheckerParams(locale: 'en-US'),
  );
  if (!context.mounted || res.isError || !res.canUpdate) return;

  await showDialog<void>(
    context: context,
    builder: (context) => AlertDialog(
      title: const Text('Update available'),
      content: Text('Version ${res.newVersion} is available. '
          'You have ${res.currentVersion}.'),
      actions: [
        TextButton(
          onPressed: () => Navigator.pop(context),
          child: const Text('Later'),
        ),
        FilledButton(
          onPressed: () => launchUrl(Uri.parse(res.appURL!)),
          child: const Text('Update'),
        ),
      ],
    ),
  );
}

Use ApkPure on Android #

const params = InStoreAppVersionCheckerParams(
  locale: 'en',
  androidStore: InStoreAppVersionCheckerAndroidStoreType.apkPure,
);
final res = await InStoreAppVersionChecker.instance.checkUpdate(params);

Use RuStore #

const params = InStoreAppVersionCheckerParams(
  locale: 'ru-RU',
  packageName: 'ru.beautybox.twa',
  currentVersion: '38.2.0',
  androidStore: InStoreAppVersionCheckerAndroidStoreType.ruStore,
);
final res = await InStoreAppVersionChecker.instance.checkUpdate(params);

RuStore identifies public catalog pages by Android package name. The checker reads SoftwareApplication.softwareVersion from the page JSON-LD. Network restrictions, missing cards, and malformed metadata are returned as errors.

Use AppGallery for any app #

const params = InStoreAppVersionCheckerParams(
  locale: 'ru-RU',
  packageName: 'ru.beautybox.twa',
  storeID: 'C107631977',
  currentVersion: '38.2.0',
  androidStore: InStoreAppVersionCheckerAndroidStoreType.appGallery,
);
final res = await InStoreAppVersionChecker.instance.checkUpdate(params);

packageName and storeID are different identifiers. The package name comes from the Android application, while AppGallery assigns a listing ID such as C107631977. Store-specific builds may use different Android package names.

This mode supports arbitrary applications, but it uses the same internal, undocumented web endpoint as the public AppGallery website. Huawei can change that endpoint or its response format without notice. The checker validates the returned App ID and, when packageName is explicitly supplied, its package. Endpoint failures and unexpected responses are returned as errors.

Use AppGallery for the installed app (official SDK) #

const params = InStoreAppVersionCheckerParams(
  locale: 'ru-RU',
  androidStore: InStoreAppVersionCheckerAndroidStoreType.appGalleryNative,
);
final res = await InStoreAppVersionChecker.instance.checkUpdate(params);

This mode uses Huawei AppUpdateClient and is the recommended AppGallery option for the installed application. It does not support arbitrary applications or packageName and currentVersion overrides. When Huawei reports no update, the response is successful with newVersion == null and canUpdate == false.

Only Huawei's NO_UPGRADE_INFO status means no update was found. Connection errors, failed checks, unknown statuses, and missing upgrade data return errors. As in the other modes, canUpdate compares version names; it does not compare Android versionCode values. Equal version names return canUpdate == false.

The plugin includes com.huawei.hms:appservice:6.16.2.300 and the Huawei Maven repository. Projects that enforce repositories in settings.gradle must also allow the Huawei repository:

dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven { url 'https://developer.huawei.com/repo/' }
    }
}

Huawei documents that checkAppUpdate checks the AppGallery server even when the AppGallery client is not installed. The client is still required when the user proceeds to the AppGallery details and installation flow. See the AppUpdateClient FAQ.

Check a specific App Store country #

const params = InStoreAppVersionCheckerParams(locale: 'en-AE');
final res = await InStoreAppVersionChecker.instance.checkUpdate(params);

This checks the UAE storefront. Choose a region where the app is available โ€” a language alone (en) is not an App Store country. See Store locale.

Check another app or override the version #

const params = InStoreAppVersionCheckerParams(
  locale: 'en-US',
  packageName: 'com.example.app', // bundle ID on iOS
  currentVersion: '1.2.3',
);

Custom HTTP client #

final checker = InStoreAppVersionChecker.instanceFor(
  httpClient: customHTTPClient,
);
final res = await checker.checkUpdate(
  const InStoreAppVersionCheckerParams(locale: 'en-US'),
);

API #

Entry point: InStoreAppVersionChecker.instance, or InStoreAppVersionChecker.instanceFor(httpClient: ...) for a custom client.

Parameters โ€” InStoreAppVersionCheckerParams #

Parameter Type Description
locale String (required) Store language/region, e.g. en-US. See Store locale.
packageName String? Android package name / iOS bundle ID. Defaults to the installed app.
storeID String? Store listing ID. Required only for AppGallery web checks, e.g. C107631977.
currentVersion String? Version to compare against. Defaults to the installed app version.
androidStore InStoreAppVersionCheckerAndroidStoreType googlePlayStore (default), apkPure, ruStore, appGallery or appGalleryNative.

If packageName or currentVersion is omitted, it is resolved from the installed app: on Android from the plugin's native implementation, on iOS from the bundle identifier and CFBundleShortVersionString. HTTP checks skip the native metadata request when both overrides are supplied. AppGallery native always reads the installed application's metadata and rejects overrides.

storeID is separate from packageName. It is required only for AppGallery web checks. AppGallery native checks obtain the installed application identity from Huawei and ignore storeID.

Response โ€” InStoreAppVersionCheckerResponse #

Field Description
isSuccess / isError Whether the lookup succeeded.
currentVersion Installed (or overridden) version.
newVersion Version published in the store, or null when unavailable.
canUpdate true if the store version is newer.
appURL Link to the store page.
errorMessage, error, stackTrace Details for error responses.

Store locale #

Use one regional locale for both platforms, for example en-US or en-AE. Google Play uses the complete locale for its hl language parameter. On iOS, the region selects the App Store storefront country; it does not automatically detect the country of the user's App Store account.

locale Google Play hl App Store country
en-US en-US us
en_AE en-AE ae
pt-BR pt-BR br
zh-Hant-TW zh-Hant-TW tw
ru ru ru (legacy country-only input)
en en Invalid country; Apple returns an error
Locale rules in detail

Supported regional forms are language-REGION and language-Script-REGION. Underscores are accepted as separators; surrounding whitespace and letter case are normalized. On Google Play, other locale forms are passed through unchanged. ApkPure and RuStore ignore locale. AppGallery web requests use it to localize the store response, but storeID remains the authoritative listing identifier. AppGallery native checks let Huawei select the locale.

On iOS, existing two-letter country-only values such as us, ae, and ru remain supported. Short values are interpreted as countries: ar means Argentina, while ar-AE explicitly selects the UAE storefront. A language such as en cannot determine a country, and the package never silently falls back to a different storefront.

iOS rejects malformed values, locales without a country such as zh-Hant, numeric regions such as es-419, and locale variants or extensions. This is structural validation, not a list of supported storefronts: Apple determines whether the requested country is supported and whether the app is available there. See Apple's country parameter documentation.

Error handling #

Always check isError before using canUpdate. A failed lookup with no store version also returns canUpdate == false; that alone does not mean the app is up to date. An error response may still report canUpdate == true if newVersion is greater.

Errors are returned (not thrown) for: invalid parameters, HTTP/network failures, malformed store data, app not found in the selected storefront, native SDK failures, and unsupported platforms.

HTTP modes return success only after reading a non-empty version string from the requested listing. An equal or older store version is still a successful check with canUpdate == false; missing or malformed version data is an error. Version names are not required to follow strict SemVer. Apple's optional listing URL remains null when absent, rather than the string "null".

An ApkPure listing with an empty or whitespace-only version returns an error with newVersion == null and canUpdate == false. Earlier versions could return newVersion == '' for this failure. Treat either value as an unavailable version when displaying the response; see the migration guide.

Google Play HTML checks read the application's identified web data, not any version-like string on the page. This undocumented web structure can change; unrecognized data and primary HTTP/network failures trigger the existing third-party PlayStoreApi fallback. If that source also fails or omits the version, the response is an error with both attempts described.

When the Google Play fallback throws a network or parsing exception, error and stackTrace retain the original fallback error and its stack trace.

Apple HTTP errors include the status, original locale, and resolved storefront. HTTP 400 includes guidance to check the country code. A successful HTTP 200 response with empty results instead reports that the app was not found in that storefront; check both its bundle ID and regional availability.

Every HTTP request has a 15-second timeout. A Google Play fallback or an AppGallery web retry can make the total check longer than one request. AppGallery native has a 15-second SDK timeout that releases the callback, plus a 20-second Dart bridge safeguard. Timeout failures return error responses. Concurrent native calls share one in-flight check, including across checker instances; after success, failure, or timeout the next call starts a new check.

Response equality and hashCode include status, versions, listing URL, and errorMessage. The identity of the diagnostic error and stackTrace objects is excluded. A success changing to an error therefore notifies a ValueNotifier even when the version fields have not changed.

Version comparison #

How versions are compared
  • Build metadata (+build) is ignored.
  • Trailing zero segments are normalized: 1.2 equals 1.2.0 (no update).
  • Release vs pre-release: a release is higher than a pre-release with the same core. 3.1.0-dev.1 to 3.1.0 is an update; the reverse is not.
  • Pre-release tokens are compared after the core version; numeric tokens numerically, mixed/alphanumeric tokens token-by-token with numeric-aware ordering.
  • Fully non-numeric current vs numeric new โ†’ update; fully non-numeric new vs numeric current โ†’ no update.
  • Whitespace is trimmed and non-alphanumeric symbols are stripped.

See the unit tests in test/unit for the authoritative behavior.

Platform integration notes #

  • Android uses Flutter 3.44+ built-in Kotlin support.
  • AppGallery native checks use Huawei App Service SDK 6.16.2.300.
  • iOS supports both Swift Package Manager (with the required FlutterFramework dependency in Package.swift) and CocoaPods.

Migration #

See MIGRATION.md for upgrade notes, including the Android package rename and the 3.0 API changes. All release notes are in the Changelog.

Development #

The repository uses mise for pinned SDKs and Just for commands, following the same setup as tetradka_app. mise.toml is the source of truth and mise.lock records download URLs and checksums. Dart comes from the pinned Flutter SDK; the package's minimum supported SDK versions remain unchanged.

mise trust
mise install
mise exec -- just precommit

With mise activated in your shell, use plain just, flutter, and dart. VS Code resolves Flutter through mise where flutter without a local FVM path. See CONTRIBUTING.md for native build prerequisites and the CocoaPods/SPM checks.

Native plugin tests run with mise exec -- just test-android-native and, on macOS, mise exec -- just test-ios-native. CI uploads Dart, Kotlin, and Swift coverage separately to Codecov. The Android suite substitutes the Huawei SDK client; it does not prove live AppGallery behavior on a physical Huawei device.

Contributing #

Issues and pull requests are welcome โ€” see CONTRIBUTING.md. If this package saves you time, please โญ star it on GitHub and ๐Ÿ‘ like it on pub.dev โ€” it helps others find it.

Funding #

If you want to support the development of the library:

Maintainers #

Anton Ustinoff (ziqq)

License #

MIT

Coverage #

14
likes
160
points
3.71k
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A lightweight Flutter plugin to check app versions in Google Play, Apple App Store, ApkPure, RuStore, and AppGallery

Repository (GitHub)
View/report issues
Contributing

Funding

Consider supporting this project:

www.buymeacoffee.com
boosty.to

License

MIT (license)

Dependencies

flutter, http, meta

More

Packages that depend on flutter_in_store_app_version_checker

Packages that implement flutter_in_store_app_version_checker