Introduction

The in_app_update_android Flutter package bridges the Play Core API and your Dart code, letting you prompt Android users to install updates without leaving the app. This is essential for delivering critical security patches, mandatory feature roll‑outs, or simply notifying users of optional upgrades.

When to Use In‑App Updates

  • Critical security fixes that must be applied before the user can continue.
  • Major feature releases where you want to encourage immediate adoption.
  • Compliance requirements that demand users run the latest version.
  • Any scenario where you prefer a seamless upgrade experience over directing users to the Play Store.

Installation

Add the package to your project with the Flutter CLI:

Code
flutter pub add in_app_update_android

After the command finishes, run flutter pub get to fetch the dependency.

Basic Setup

The package works out‑of‑the‑box on Android devices that are distributed via Google Play. No additional native configuration is required, but you should be aware of the following:

  • The app must be signed with the same keystore that is uploaded to the Play Console.
  • In‑app updates are only available for apps that are published on Google Play (they do not work for side‑loaded APKs).
  • Make sure your minSdkVersion is at least 21, as required by Play Core.

Implementing an Update Check

Below is a minimal, production‑ready example that checks for an update on startup and launches an immediate update flow if one is available. The code also guards against non‑Android platforms.

Dart / Flutter
import 'package:flutter/material.dart';
import 'package:in_app_update_android/in_app_update_android.dart';
import 'dart:io';

class UpdateChecker extends StatefulWidget {
  const UpdateChecker({Key? key}) : super(key: key);

  @override
  State createState() => _UpdateCheckerState();
}

class _UpdateCheckerState extends State {
  @override
  void initState() {
    super.initState();
    _checkForUpdate();
  }

  Future _checkForUpdate() async {
    if (!Platform.isAndroid) return; // Skip iOS, web, desktop
    try {
      final info = await InAppUpdate.checkForUpdate();
      if (info.updateAvailability == UpdateAvailability.updateAvailable) {
        // Choose the update mode that matches your policy
        await InAppUpdate.performImmediateUpdate();
      }
    } catch (e) {
      // Errors can include Play Core not being available or network issues
      debugPrint('Update check failed: $e');
    }
  }

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Center(child: Text('Your app content here')),
    );
  }
}

This snippet demonstrates the core workflow:

  • Call InAppUpdate.checkForUpdate() to retrieve UpdateInfo.
  • Inspect updateAvailability – if an update is available, trigger either performImmediateUpdate() or performFlexibleUpdate().

Flexible Updates

If you prefer the user to continue using the app while the update downloads in the background, replace the immediate call with:

Code
await InAppUpdate.startFlexibleUpdate();
// Later, when you want to complete the installation:
await InAppUpdate.completeFlexibleUpdate();

Remember to surface UI that shows download progress and a button to “Restart” once the update is ready. The package does not provide a built‑in progress UI, so you’ll need to listen to InAppUpdate.flexibleUpdateProgressStream (if available) or implement your own using platform channels.

Tip: Use flexible updates for large feature releases where interrupting the user would be disruptive. Immediate updates are best for critical patches.

Common Mistakes & How to Avoid Them

  • Assuming it works on iOS. The package is Android‑only; guard calls with Platform.isAndroid to prevent runtime errors.
  • Forgetting Play Store distribution. In‑app updates require the app to be installed from Google Play. Side‑loaded builds will always receive UpdateAvailability.noUpdateAvailable.
  • Neglecting user experience. Overusing immediate updates can frustrate users. Reserve them for truly mandatory patches.
  • Missing error handling. Network failures, Play Core unavailability, or version code mismatches should be caught and logged; otherwise the app may appear frozen.

Testing In‑App Updates

Google Play provides an internal testing track that lets you upload a new APK/AAB and simulate update flows. Follow these steps:

  1. Upload a higher versionCode to an internal test track.
  2. Enroll your test device in the track via the Play Store.
  3. Run the app on the device; the update check should detect the newer version.

If you cannot use the Play Console, you can mock the response by wrapping the update call in a service that returns a fabricated UpdateInfo during development.

Full Example with Flexible UI

The following example adds a simple progress indicator for a flexible update. It demonstrates how to listen for download progress and prompt the user to restart when the update is ready.

Dart / Flutter
import 'package:flutter/material.dart';
import 'package:in_app_update_android/in_app_update_android.dart';
import 'dart:io';

class FlexibleUpdateDemo extends StatefulWidget {
  const FlexibleUpdateDemo({Key? key}) : super(key: key);

  @override
  State<FlexibleUpdateDemo> createState() => _FlexibleUpdateDemoState();
}

class _FlexibleUpdateDemoState extends State<FlexibleUpdateDemo> {
  double _downloadProgress = 0.0;
  bool _updateReady = false;

  @override
  void initState() {
    super.initState();
    _checkAndStartFlexibleUpdate();
  }

  Future _checkAndStartFlexibleUpdate() async {
    if (!Platform.isAndroid) return;
    try {
      final info = await InAppUpdate.checkForUpdate();
      if (info.updateAvailability == UpdateAvailability.updateAvailable) {
        await InAppUpdate.startFlexibleUpdate();
        // Listen to progress – the actual API may expose a stream; adjust as needed.
        InAppUpdate.flexibleUpdateProgressStream.listen((progress) {
          setState(() => _downloadProgress = progress / 100.0);
        }, onDone: () async {
          setState(() => _updateReady = true);
        });
      }
    } catch (e) {
      debugPrint('Flexible update error: $e');
    }
  }

  Future _completeUpdate() async {
    try {
      await InAppUpdate.completeFlexibleUpdate();
    } catch (e) {
      debugPrint('Complete update failed: $e');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Flexible Update Demo')),
      body: Center(
        child: _updateReady
            ? ElevatedButton(
                onPressed: _completeUpdate,
                child: const Text('Restart to Apply Update'),
              )
            : Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  const Text('Downloading update...'),
                  const SizedBox(height: 12),
                  LinearProgressIndicator(value: _downloadProgress),
                ],
              ),
      ),
    );
  }
}

Adjust the progress handling to match the actual stream provided by the package version you are using. The example showcases the typical UI flow: show a progress bar, then a restart button when the download finishes.

Conclusion

Integrating the in_app_update_android Flutter package gives you native‑level control over Android update experiences without leaving the Flutter codebase. By following the steps above—installing the package, checking for updates, choosing the appropriate update mode, and handling edge cases—you can keep your user base on the latest, most secure version of your app.

Frequently Asked Questions

Does in_app_update_android work on iOS or web?

No. The package is Android‑only because it relies on Google Play's Play Core library. Guard calls with Platform.isAndroid to avoid runtime errors on other platforms.

What is the difference between immediate and flexible updates?

Immediate updates block the user until the new version is installed, suitable for critical patches. Flexible updates download in the background, allowing the user to continue using the app and prompting a restart when the download completes.

Do I need to add any native code or permissions?

No additional native code is required. The package includes the necessary Play Core binaries. Ensure your app is distributed via Google Play and that the minSdkVersion is at least 21.

How can I test the update flow before publishing to production?

Upload a higher versionCode to an internal testing track in the Play Console, enroll your test device, and run the app. The update check will detect the newer version, letting you verify both immediate and flexible flows.

What should I do if the update check fails with an exception?

Catch the exception, log it for diagnostics, and optionally fall back to a manual Play Store link. Common causes are network issues, Play Core not being available, or the app not being installed from Google Play.