in_app_version_update
A lightweight Flutter helper to detect and run app updates on Android (Play In‑App Update) and to present an iOS App Store update dialog.
This package wraps platform-specific update flows and provides a compact, testable API so host apps don't need to depend on in_app_update or implement App Store lookups themselves.

Key capabilities
- Android
- Immediate updates (blocking) using Google Play In‑App Update.
- Flexible updates (download in background) with a choice of auto-completion or host-controlled completion.
- Exposes install status updates (stream + optional callback) so host apps can show progress UI and decide when to call
completeFlexibleUpdate().
- iOS
- App Store lookup by app id and an optional, testable Cupertino update dialog.
- All dialog texts are configurable via parameters so host apps can localize or customize wording.
iosAppIdis optional — if your app only targets Android you can omit it. When iOS flows are used the id must be provided.
- Platform control
checkForUpdateacceptscheckAndroidandcheckIosflags so host apps can selectively run platform checks; both default totrue.
The repository includes an example/ app demonstrating immediate and flexible flows, stream and callback usage, auto completion, and iOS dialog usage. Unit tests cover version comparison and mapping logic; widget tests verify the iOS dialog and example screens.
Why use this package
- Single cross-platform API (
InAppVersionUpdate) for app update flows. - Keeps host apps decoupled from
in_app_updateand App Store lookup details. - Stream/callback visibility into install state for better UX (progress, prompts, manual completion).
- Testable helpers (presenting dialog UI without network calls) to simplify unit and widget tests.
Quick start
- Add the package to your
pubspec.yaml(when published use a version; for local development the example imports the library directly):
dependencies:
in_app_version_update: ^<latest-version>
- Create an instance and check for updates:
Future<void> exampleCheck(BuildContext context) async {
final updater = InAppVersionUpdate(iosAppId: '123456789');
await updater.checkForUpdate(context);
}
- Example: Android flexible update with host-controlled completion (listen to stream):
Future<void> flexibleExample(BuildContext context) async {
final updater = InAppVersionUpdate(iosAppId: '123456789');
await updater.checkForUpdate(
context,
androidUpdateType: AndroidUpdateType.flexible,
autoCompleteFlexible: false,
);
final sub = updater.installUpdateStream.listen((status) async {
if (status == InstallStatus.downloaded) {
// show prompt to user and then call to install
await updater.completeFlexibleUpdate();
}
});
// Cancel subscription when no longer needed
await sub.cancel();
}
- Example: Provide a callback to receive install updates (helper keeps an internal subscription you can stop):
Future<void> callbackExample(BuildContext context) async {
final updater = InAppVersionUpdate(iosAppId: '123456789');
await updater.checkForUpdate(
context,
androidUpdateType: AndroidUpdateType.flexible,
autoCompleteFlexible: false,
onInstallStatus: (status) async {
if (status == InstallStatus.downloaded) {
// prompt user and then complete
await updater.completeFlexibleUpdate();
await updater.stopInstallStatusCallback();
}
},
);
}
- iOS: present a configurable dialog (host can customize all texts). The helper also provides
presentIosUpdateDialog()to show dialog UI directly (useful for widget tests):
Future<void> presentIosDialogExample(BuildContext context) async {
final updater = InAppVersionUpdate(iosAppId: '123456789');
await updater.presentIosUpdateDialog(
context,
title: 'Update available',
content: 'A new version is available on the App Store.',
laterText: 'Not now',
updateNowText: 'Open App Store',
onUpdatePressed: () {
// optional test hook instead of launching the App Store
},
);
}
API reference
-
Constructor
InAppVersionUpdate({ String? iosAppId, Duration httpTimeout = const Duration(seconds: 10) })iosAppId: Optional numeric App Store id used for iOS lookups. Omit when you only need Android.httpTimeout: network timeout for App Store lookup requests.
-
checkForUpdate
Future<void> checkForUpdate(BuildContext context, { AndroidUpdateType androidUpdateType = AndroidUpdateType.immediate, bool autoCompleteFlexible = true, void Function(InstallStatus)? onInstallStatus, String iosDialogTitle = 'Update available', String iosDialogContent = 'A newer version of this app is available on the App Store.', String iosLaterButtonText = 'Later', String iosUpdateNowButtonText = 'Update now', bool checkAndroid = true, bool checkIos = true })androidUpdateType: chooseAndroidUpdateType.immediateor.flexible.autoCompleteFlexible: Whentruethe helper automatically callscompleteFlexibleUpdate()once a flexible update has been downloaded. Iffalse, the host app must callcompleteFlexibleUpdate()when appropriate.onInstallStatus: Optional callback that receives mappedInstallStatusvalues during flexible update lifecycle. If used, the helper keeps an internal subscription untilstopInstallStatusCallback()is called.iosDialogTitle,iosDialogContent,iosLaterButtonText,iosUpdateNowButtonText: All dialog texts are configurable; they default to sensible values.checkAndroid,checkIos: Per-platform boolean flags (both default totrue). Use these to skip checks on a platform.
-
installUpdateStream
Stream<InstallStatus> get installUpdateStream- Emits mapped
InstallStatusvalues (enum documented below). Use this when you want host-controlled completion and progress UI.
- Emits mapped
-
completeFlexibleUpdate
Future<void> completeFlexibleUpdate()— calls Play's API to install a downloaded flexible update.
-
stopInstallStatusCallback
Future<void> stopInstallStatusCallback()— cancels the internal subscription whenonInstallStatuswas provided.
-
presentIosUpdateDialog
Future<void> presentIosUpdateDialog(BuildContext context, { String title = 'Update available', String content = 'A newer version of this app is available on the App Store.', String laterText = 'Later', String updateNowText = 'Update now', VoidCallback? onUpdatePressed })- Presents the iOS update dialog UI immediately (no network checks). Useful for widget tests or when the host app wants to control the Update action. If
iosAppIdis not provided andonUpdatePressedisnullthe method will log a debug message and return without showing the dialog.
- Presents the iOS update dialog UI immediately (no network checks). Useful for widget tests or when the host app wants to control the Update action. If
Enums
AndroidUpdateType { immediate, flexible }InstallStatus { unknown, pending, downloading, downloaded, installing, installed, failed, canceled }
Testing
-
Unit tests
- Version comparison logic (
isStoreVersionNewer) and mapping fromin_app_updatestatuses to the packageInstallStatusare covered by unit tests. Edge cases include different segment lengths ("1.2" vs "1.2.0"), pre-release suffixes, and invalid inputs.
- Version comparison logic (
-
Widget tests
- iOS dialog UI is tested via
presentIosUpdateDialogto avoid network calls. - Example screens in
example/are covered by widget tests to verify the UI and that flows present the expected controls.
- iOS dialog UI is tested via
Run tests:
flutter test
Notes and platform specifics
- Android in-app updates require the Play Store and a real device for full flow validation. Tests intentionally avoid running Play Core flows and instead validate mapping and UI.
- iOS App Store lookup uses a network request to Apple's public lookup API; use a short timeout and handle network failures gracefully in your app.
License
This project is provided under the terms of the LICENSE file in the repository.
Acknowledgements
This package uses the in_app_update package for Android in-app update integration: https://pub.dev/packages/in_app_update
Many thanks to the author(s) and maintainers of in_app_update for their excellent work — this project builds on top of their efforts.
Libraries
- in_app_version_update
- In-app version update helper for Flutter