Docs Flutter Plugin

SK Auto-Updater

Flutter plugin for in-app updates

A fully configurable auto-update plugin for Flutter Android apps. Check for updates, download APKs, and trigger installation automatically.

Flutter Package Android Only MIT License
Download .md

Platform Support

✓

Android

✕

iOS

✕

Web

✕

macOS

✕

Windows

✕

Linux

This plugin only supports Android. Designed for sideloaded apps, internal/enterprise apps, and apps distributed outside the Google Play Store.

Requirements

This plugin requires a ReleaseHub backend to serve version information and APK files:

Hosted

Use releasehub.dev - managed service by Version Two

Self-hosted

Deploy your own ReleaseHub instance on your server

Important: ReleaseHub (either hosted or self-hosted) is required for this plugin to function. The plugin communicates with ReleaseHub's API to check for updates and download files.

Installation

Add to your pubspec.yaml:

dependencies:
  releasehub_updater: ^1.0.0

Then run:

$ flutter pub get

GetX Adapter (Optional)

If using the GetX adapter, ensure you have GetX in your dependencies:

dependencies:
  get: ^4.6.6

Android Configuration

Required Permissions

Add to your android/app/src/main/AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- Required for downloading updates -->
    <uses-permission android:name="android.permission.INTERNET" />

    <!-- Required for installing APKs -->
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

    <!-- Required for Android 9 and below -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
        android:maxSdkVersion="28" />

    <application ...>
        <!-- Your activities and other components -->
    </application>
</manifest>

File Provider Configuration (Android 7+)

The open_filex package usually handles this automatically. If APK installation fails, add to your AndroidManifest.xml inside the <application> tag:

<provider
    android:name="androidx.core.content.FileProvider"
    android:authorities="${applicationId}.fileProvider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>

And create android/app/src/main/res/xml/file_paths.xml:

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path name="external_files" path="." />
    <external-files-path name="external_files_path" path="." />
    <cache-path name="cache" path="." />
</paths>

Quick Start

Simple Usage (Recommended)

The easiest way to use the plugin with built-in UI:

import 'package:releasehub_updater/autoupdater.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // Initialize the auto-updater with ReleaseHub
  await AutoUpdater.init(
    baseUrl: 'https://releases.example.com',  // your ReleaseHub server URL
    projectSlug: 'my-app',
    channel: 'stable',
  );

  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      // Required for dialogs to work
      navigatorKey: AutoUpdater.navigatorKey,
      scaffoldMessengerKey: AutoUpdater.scaffoldMessengerKey,
      home: HomeScreen(),
    );
  }
}

// Manual check from settings screen
ElevatedButton(
  onPressed: () => AutoUpdater.checkForUpdates(),
  child: Text('Check for Updates'),
)

Standalone Mode (No External Dependencies)

Uses Flutter's built-in ValueNotifier for reactive state:

import 'package:releasehub_updater/autoupdater_standalone.dart';

// Create the service
final updater = AutoUpdaterStandalone(
  config: AutoUpdaterConfig.releaseHub(
    baseUrl: 'https://releases.example.com',
    projectSlug: 'my-app',
    channel: 'stable',
  ),
  ui: AutoUpdaterDefaultUI.create(
    primaryColor: Colors.blue,
  ),
);

// Initialize (call once at app startup)
await updater.initialize();

// Use ValueListenableBuilder for reactive UI
ValueListenableBuilder<bool>(
  valueListenable: updater.isCheckingForUpdate,
  builder: (context, isChecking, child) {
    return isChecking
      ? CircularProgressIndicator()
      : ElevatedButton(
          onPressed: () => updater.checkForUpdate(
            silent: false,
            context: context,
          ),
          child: Text('Check for Updates'),
        );
  },
)

// Don't forget to dispose when done
updater.dispose();

GetX Integration

import 'package:releasehub_updater/autoupdater_getx.dart';

// Register the service
Get.put(
  AutoUpdaterGetxService(
    config: AutoUpdaterConfig.releaseHub(
      baseUrl: 'https://releases.example.com',
      projectSlug: 'my-app',
      channel: 'stable',
    ),
  ),
  permanent: true,
);

// Use in widgets
final updater = Get.find<AutoUpdaterGetxService>();
Obx(() => updater.isCheckingForUpdate.value
  ? CircularProgressIndicator()
  : Text('Version: ${updater.currentVersion?.version}')
)

Core Service (Maximum Control)

import 'package:releasehub_updater/autoupdater.dart';

final core = AutoUpdaterCore(
  config: AutoUpdaterConfig.releaseHub(
    baseUrl: 'https://releases.example.com',
    projectSlug: 'my-app',
  ),
);

await core.initialize();

final result = await core.checkForUpdate();

switch (result) {
  case UpdateAvailable(versionInfo: final info):
    print('Update available: ${info.displayVersion}');
    // Download and install manually
    final downloadResult = await core.downloadApk(
      info.apkUrl,
      info.displayVersion,
      onProgress: (progress) => print(progress.formattedProgress),
    );
  case NoUpdateAvailable():
    print('Already on latest version');
  case UpdateCheckError(message: final msg):
    print('Error: $msg');
  case UpdateCheckDisabled():
    print('Updates disabled');
}

Configuration

AutoUpdaterConfig

PropertyTypeDefaultDescription
baseUrlStringrequiredReleaseHub URL (hosted or self-hosted)
appIdStringrequiredProject slug in ReleaseHub
versionPathString'api/check'API path (auto-set in ReleaseHub mode)
environmentString'stable'Release channel name
releaseHubModebooltrueEnable ReleaseHub API format
checkOnStartupbooltrueAuto-check on initialization
startupDelayDuration3 secondsDelay before startup check
skipPermissionCheckboolfalseSkip Android permission dialogs
isDisabledboolfalseCompletely disable updates
apkFilenamePatternString'{appId}_update_{version}.apk'Downloaded APK filename
includeArchitecturebooltrueSend device arch to server
httpHeadersMap?nullCustom HTTP headers (for API auth)
connectionTimeoutDuration30 secondsHTTP request timeout
responseFieldsVersionResponseFields?nullCustom JSON field mapping for non-standard APIs
loggerFunction?nullCustom logging callback (String) => void
dialogAutoDismissDelayDuration?8 secondsAuto-dismiss update dialog after delay (null to disable)

Note: When using AutoUpdaterConfig.releaseHub(), the defaults are optimized for ReleaseHub. When using the base constructor directly, defaults differ: versionPath='version', environment='prod', releaseHubMode=false.

Project Visibility & Authentication

Public Projects (No Authentication)

await AutoUpdater.init(
  baseUrl: 'https://releases.example.com',
  projectSlug: 'my-public-app',
  channel: 'stable',
);

Private Projects (API Key Authentication)

await AutoUpdater.initWithConfig(
  config: AutoUpdaterConfig.releaseHub(
    baseUrl: 'https://releases.example.com',
    projectSlug: 'my-private-app',
    channel: 'stable',
    httpHeaders: {
      'Authorization': 'Bearer YOUR_API_KEY',
    },
  ),
);

Customizing UI

Custom Strings (Localization)

AutoUpdater.init(
  baseUrl: 'https://releases.example.com',
  projectSlug: 'my-app',
  strings: AutoUpdaterStrings(
    updateAvailable: 'Aktualizácia dostupná',
    download: 'Stiahnuť',
    later: 'Neskôr',
    noUpdateAvailable: 'Mate najnovsiu verziu',
  ),
);

Custom UI Callbacks (Standalone)

AutoUpdaterStandalone(
  config: myConfig,
  ui: AutoUpdaterStandaloneUI(
    onShowUpdateAvailable: (context, info, onDownload) {
      showDialog(
        context: context!,
        builder: (ctx) => MyCustomUpdateDialog(
          version: info.displayVersion,
          onDownload: () {
            Navigator.pop(ctx);
            onDownload();
          },
        ),
      );
    },
    onShowError: (context, title, message) {
      MyToast.showError('$title: $message');
    },
  ),
);

Architecture Detection

The plugin automatically detects the Android device's CPU architecture and:

  • Sends it as a query parameter (?arch=arm64-v8a) to ReleaseHub
  • Normalizes build numbers for Flutter split APK builds (--split-per-abi)

Supported Android Architectures

arm64-v8a

Most modern devices

armeabi-v7a

Older 32-bit ARM

x86_64

Emulators, Chromebooks

x86

Older emulators

Debug Mode

For troubleshooting update detection issues:

// Get debug info
print(AutoUpdater.getDebugInfo());

// Show debug dialog
AutoUpdater.showDebugDialog();

Output includes:

  • Current app version and build number
  • Device architecture
  • ReleaseHub URL being used
  • Raw vs normalized build numbers

Using Custom Backend (Non-ReleaseHub)

If you have your own backend, disable ReleaseHub mode:

AutoUpdaterConfig(
  baseUrl: 'https://your-server.com',
  appId: 'com.example.app',
  versionPath: 'version',
  environment: 'prod',
  releaseHubMode: false,  // Use standard mode
  responseFields: VersionResponseFields(
    version: 'app_version',
    build: 'build_number',
    apkUrl: 'download_url',
  ),
)

Custom Backend Response Format:

{
  "app_version": "1.2.0",
  "build_number": 42,
  "download_url": "https://your-server.com/app-1.2.0.apk"
}

Disabling in Development

To prevent update checks during development:

import 'package:flutter/foundation.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // Only enable auto-updater in release builds
  if (!kDebugMode) {
    await AutoUpdater.init(
      baseUrl: 'https://releases.example.com',
      projectSlug: 'my-app',
    );
  }

  runApp(MyApp());
}