App Updater

GitHub stars pub package

Check for app updates and show platform-native dialogs. Supports iOS, Android, macOS, Windows, and Linux with adaptive UI for each platform.

Screenshot

Features

  • Platform-Specific Dialogs: Native UI for each platform (Material, Cupertino, Fluent, Adwaita)
  • Persistent Dialogs: Force users to update with non-dismissible dialogs
  • Skip Version: Let users skip specific versions
  • Do Not Ask Again: Remember user's preference to not show update dialogs
  • Custom Endpoints: Support for custom XML/JSON version endpoints

Supported Platforms

Platform Store Dialog Style
iOS App Store Cupertino
Android Play Store Material Design 3
macOS Mac App Store Cupertino
Windows Microsoft Store Fluent Design
Linux Snap Store / Flathub Adwaita (GNOME)

Installation

Add app_updater to your pubspec.yaml:

dependencies:
  app_updater: ^3.0.0

Quick Start

import 'package:app_updater/app_updater.dart';

// Create an AppUpdater instance
final appUpdater = AppUpdater.configure(
  iosAppId: '123456789',
  macAppId: '987654321',
  microsoftProductId: '9NBLGGH4NNS1',
  snapName: 'my-app',
  flathubAppId: 'com.example.myapp',
);

// Check for updates and show dialog
final updateInfo = await appUpdater.checkAndShowUpdateDialog(context);

if (updateInfo.updateAvailable) {
  print('New version available: ${updateInfo.latestVersion}');
}

Usage

Basic Usage

// Create AppUpdater with configuration
final appUpdater = AppUpdater.configure(
  iosAppId: '123456789',
  // androidPackageName is auto-detected!
);

// Check and show dialog if update available
await appUpdater.checkAndShowUpdateDialog(context);

All Platforms

final appUpdater = AppUpdater.configure(
  // Mobile
  iosAppId: '123456789',
  androidPackageName: 'com.example.app', // Optional - auto-detected
  // Desktop
  macAppId: '987654321',
  microsoftProductId: '9NBLGGH4NNS1',
  snapName: 'my-app',
  flathubAppId: 'com.example.myapp',
  linuxStoreType: LinuxStoreType.snap, // or LinuxStoreType.flathub
);

Dialog Styles

The package provides 5 dialog styles that match each platform's design language:

await appUpdater.showUpdateDialog(
  context,
  dialogStyle: UpdateDialogStyle.adaptive, // Auto-selects based on platform
);

// Or force a specific style:
// UpdateDialogStyle.material   - Material Design 3 (Android)
// UpdateDialogStyle.cupertino  - iOS/macOS native
// UpdateDialogStyle.fluent     - Windows Fluent Design
// UpdateDialogStyle.adwaita    - Linux GNOME/Adwaita

Persistent (Forced) Updates

For critical updates that users must install:

await appUpdater.checkAndShowUpdateDialog(
  context,
  isPersistent: true,  // Cannot be dismissed
  isDismissible: false,
  title: 'Critical Update Required',
  message: 'Please update to continue using the app.',
);

Skip Version & Do Not Ask Again

Let users control update notifications:

await appUpdater.checkAndShowUpdateDialog(
  context,
  showSkipVersion: true,    // Shows "Skip this version" option
  showDoNotAskAgain: true,  // Shows "Don't remind me again" option
);

Managing Preferences

// Check if user skipped a version
final isSkipped = await UpdatePreferences.isVersionSkipped('2.0.0');

// Check if user chose "do not ask again"
final doNotAsk = await UpdatePreferences.isDoNotAskAgain();

// Reset all preferences
await UpdatePreferences.clearAll();

// Reset only skipped version
await UpdatePreferences.clearSkippedVersion();

Custom Update Server (JSON)

Host your own version endpoint:

{
  "version": "2.0.0",
  "url": "https://example.com/download"
}
final appUpdater = AppUpdater.configure(
  customJsonUrl: 'https://example.com/version.json',
);

Custom Update Server (XML)

<?xml version="1.0" encoding="UTF-8"?>
<app>
  <version>2.0.0</version>
  <url>https://example.com/download</url>
</app>
final appUpdater = AppUpdater.configure(
  customXmlUrl: 'https://example.com/version.xml',
);

Check Without Dialog

final updateInfo = await appUpdater.checkForUpdate();

print('Current: ${updateInfo.currentVersion}');
print('Latest: ${updateInfo.latestVersion}');
print('Update available: ${updateInfo.updateAvailable}');
print('Store URL: ${updateInfo.updateUrl}');

Open Store

// Open the appropriate store for the current platform
await appUpdater.openStore();

// Or use the convenience function
await openAppStore(
  iosAppId: '123456789',
  microsoftProductId: '9NBLGGH4NNS1',
);

Callbacks

await appUpdater.checkAndShowUpdateDialog(
  context,
  onNoUpdate: () {
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(content: Text('App is up to date!')),
    );
  },
  onUpdate: () {
    print('User chose to update');
  },
  onCancel: () {
    print('User cancelled');
  },
);

Custom Dialog

await appUpdater.showUpdateDialog(
  context,
  customDialog: AlertDialog(
    title: const Text('Custom Update Dialog'),
    content: const Text('A new version is available!'),
    actions: [
      TextButton(
        onPressed: () => Navigator.pop(context),
        child: const Text('Later'),
      ),
      FilledButton(
        onPressed: () {
          Navigator.pop(context);
          appUpdater.openStore();
        },
        child: const Text('Update'),
      ),
    ],
  ),
);

API Reference

AppUpdater

Method Description
AppUpdater.configure(...) Create instance with individual parameters
AppUpdater(config) Create instance with AppUpdaterConfig
checkForUpdate() Check for updates, returns UpdateInfo
showUpdateDialog(context, ...) Show update dialog
checkAndShowUpdateDialog(context, ...) Check and show dialog if update available
openStore() Open the appropriate app store
getStoreUrl() Get store URL for current platform

UpdateDialogStyle

Style Platform Description
adaptive All Auto-selects based on platform
material Android Material Design 3
cupertino iOS/macOS Native Apple design
fluent Windows Microsoft Fluent Design
adwaita Linux GNOME/Adwaita style

UpdateInfo

Property Type Description
currentVersion String Current app version
latestVersion String? Latest available version
updateUrl String? Store/download URL
updateAvailable bool Whether update is available

UpdatePreferences

Method Description
isVersionSkipped(version) Check if version is skipped
skipVersion(version) Skip a specific version
isDoNotAskAgain() Check "do not ask again" preference
setDoNotAskAgain(value) Set "do not ask again" preference
clearAll() Clear all preferences
clearSkippedVersion() Clear skipped version only

Migration from v2.x

// Before (v2.x)
final updateInfo = await checkAppUpdate(
  context,
  iosAppId: '123456789',
);

// After (v3.x)
final appUpdater = AppUpdater.configure(
  iosAppId: '123456789',
);
final updateInfo = await appUpdater.checkAndShowUpdateDialog(context);

Notes

  • Update dialogs won't work on iOS Simulator (no App Store)
  • Android package name is auto-detected if not provided
  • Custom endpoints take priority over store checks

Credits

Author

Libraries

app_updater