dartpub.dev DartNative · beta
plugins / in_app_update_kit
in

in_app_update_kit

v0.1.0 MIT

Google Play in-app updates for DartNative, immediate and flexible, with the in_app_update API.

Google Play In-App Updates for DartNative apps, with the Flutter in_app_update API: checkForUpdate(), performImmediateUpdate(), startFlexibleUpdate() and completeFlexibleUpdate(). It runs on Play Core's AppUpdateManager (com.google.android.play:app-update 2.1.0) over FFI and JNI; minSdk 24. Android only: on iOS checkForUpdate reports availability unknown and every start is a safe no-op, so one code path serves both platforms. A real update needs a build installed from Google Play, such as the internal test track. A fake AppUpdateManager for tests is in package:in_app_update_kit/testing.dart. API credited to in_app_update (MIT).

by AbdurahmanAlmehdi/in_app_update_kit · DartNative ≥ 3.0 · updated today
Install
Free
pubspec.yaml
dependencies:
  in_app_update_kit:
    hosted: https://dartpub.dev
    version: ^0.1.0
Weekly installs
0
Active apps
0
Rating
0.0 · 0
Open issues
0

in_app_update_kit

Google Play In-App Updates for DartNative apps. It keeps the Flutter in_app_update API, so code written for it ports by changing one import:

// import 'package:in_app_update/in_app_update.dart';
import 'package:in_app_update_kit/in_app_update_kit.dart';

Calls go over FFI and JNI to Play's AppUpdateManager; there are no platform channels. Android only. On iOS every call is a safe no-op (see below).

Install

dependencies:
  in_app_update_kit:
    hosted: https://dartpub.dev
    version: ^0.1.0
dn pub get   # regenerates lib/dartnative_plugin_registrant.dart

DartNativePluginRegistrant.registerAll() in main() loads the plugin. No other setup is needed.

Use

Future<void> updateIfNeeded({required bool forced}) async {
  try {
    final info = await InAppUpdate.checkForUpdate();
    if (info.updateAvailability != UpdateAvailability.updateAvailable) return;
    if (forced) {
      await InAppUpdate.performImmediateUpdate();
    } else if (await InAppUpdate.startFlexibleUpdate() ==
        AppUpdateResult.success) {
      // Downloaded. Install when it suits the user; Play restarts the app.
      await InAppUpdate.completeFlexibleUpdate();
    }
  } on InAppUpdateException catch (e) {
    // e.isUnavailable: Play can't update this install (sideloaded build,
    // no Play Store). Fall back to opening the store listing.
  }
}
Call Result
checkForUpdate() AppUpdateInfo. Must run before a start.
performImmediateUpdate() Play's full-screen flow. success, userDeniedUpdate or inAppUpdateFailed; Play usually restarts the app first.
startFlexibleUpdate() Consent dialog, then a background download. success once the download finished; userDeniedUpdate if the dialog was declined or the download canceled.
completeFlexibleUpdate() Installs the downloaded update.
installUpdateListener Stream<InstallStatus> of Play's install states.

Platform setup

Android

Nothing to add to the app. The plugin's Gradle module depends on com.google.android.play:app-update:2.1.0, which pulls in com.google.android.play:core-common and the Play services basement and tasks libraries. Gradle fetches them from Google's Maven repository; this package doesn't bundle or modify them. They stay under their own terms: the Play Core Software Development Kit Terms of Service and the Android Software Development Kit License. core-common merges Play's transparent PlayCoreDialogWrapperActivity into your manifest. No permissions are added. R8 rules ship with the plugin.

In-app updates only work for builds installed from Google Play. A debug build from dn run makes checkForUpdate() throw TASK_FAILURE with installErrorCode -10 (ERROR_APP_NOT_OWNED), or return updateNotAvailable, depending on the Play Store version. To test the real flow, use Play's internal app sharing or an internal test track.

iOS

iOS has no in-app update API, and the package ships no iOS code, pod or symbols. The calls still work and never throw:

  • checkForUpdate() returns updateAvailability: unknown, nothing allowed.
  • performImmediateUpdate() and startFlexibleUpdate() return inAppUpdateFailed and show nothing.
  • completeFlexibleUpdate() does nothing; installUpdateListener never emits.

Send iOS users to the App Store yourself.

Errors

Failures throw InAppUpdateException (Flutter's PlatformException doesn't exist in DartNative). code keeps in_app_update's strings:

code When
TASK_FAILURE A Play task failed; installErrorCode holds Play's InstallErrorCode.
REQUIRE_CHECK_FOR_UPDATE A start or complete before checkForUpdate().
REQUIRE_FOREGROUND_ACTIVITY No Activity in the foreground.
INSTALL_ERROR The flexible download failed.
UPDATE_IN_PROGRESS A start while another start is pending.
NATIVE_UNAVAILABLE The native library didn't load.

isUnavailable is true for install error codes -3, -9 and -10, where Play can't serve updates to this install at all.

Differences from in_app_update

  • InAppUpdateException instead of PlatformException, with the same codes.
  • Unknown Play values (such as InstallStatus 10, REQUIRES_UI_INTENT) decode as unknown instead of throwing a StateError.
  • AppUpdateInfo == compares the precondition lists by value.
  • A flexible download canceled from the notification completes startFlexibleUpdate() with userDeniedUpdate; upstream never completed. Upstream also added a new install listener on every start.
  • A second start while one is pending throws UPDATE_IN_PROGRESS instead of orphaning the first call.
  • On iOS the calls are no-ops instead of throwing MissingPluginException.

Testing

package:in_app_update_kit/testing.dart has two seams:

  • debugSetInAppUpdateBackend(backend) routes InAppUpdate through a fake InAppUpdateBackend in pure-Dart tests.
  • debugUseFakeAppUpdateManager() (Android debug builds) swaps in Play's FakeAppUpdateManager, so the flexible flow can be walked on an emulator. The example app has buttons for it.
cd example && dn run -d <android-device>

Credits & license

The API, the enums and AppUpdateInfo, their documentation and the Play call sequence are adapted from in_app_update 5.0.0 by Victor Choueiri (MIT). The method and event channels were replaced by an FFI/JNI bridge, and the flexible flow's listener leak and cancel hang were fixed. See THIRD_PARTY_NOTICES.

This package is MIT, see LICENSE.